2012-05-04 6 views
5

Я столкнулся с действительно странной ошибкой? на mysql + php прямо сейчас. ли простой выбор, в следующем примере я использую несколько полей, чтобы попытаться объяснить мою проблему:MySQL круглая странная ошибка

  • «поле» является 11,5
  • $ phpvar является 1,15

MySQL запросов:

select round(field * " . $phpvar . " ,2) as a1, 
     round(field * 1.15 ,2) as a2, 
     round(11.5 * " . $phpvar . " ,2) as a3, 
     round(11.5 * 1.15 ,2) as a4, 
     field * " . $phpvar . " as a5 
from ... 

ОК, я пытаюсь получить 13.23. "field" * $phpvar = 13.225, поэтому с помощью раунда (13.225,2) я должен получить 13.23, правильно? ну да и нет.

Результаты запроса:

  • a1 [круглый (". $ Phpvar" поле *, 2)] => 13,22
  • a2 [круглый (поле * 1,15, 2)] => 13,22
  • а3 [круглая (11.5 * "$ phpvar.", 2)] => 13.23
  • а4 [круглый (11,5 * 1,15, 2)] => 13,23
  • а5 [поле * ". $ phpvar. "] => 13.225 (без раунда)

Что мне не хватает? как это возможно, когда дело доходит до использования «поля», мой результат получает подделку?

+1

Как определить поле в базе данных? – Anigel

+0

выберите поле * 1.15 и 11.5 * 1.15 без округления, а также a6 и a7 и опубликуйте результат. – jishi

+0

Просто используйте 'ceil()' вместо этого? – Nick

ответ

6

Проблема заключается в сохранении значений DOUBLE и FLOAT.

Возможно, что значения, такие как 11.5 или 22.475 coul, могут быть сохранены в приблизительных значениях, таких как 11.499999999999 ~ или 22.475000000000000001, поэтому некоторые вычисления или округления могут привести к неправильным результатам.

Всегда лучше хранить значения с плавающей точкой в ​​размере DECIMAL, где значение хранится точно со всеми десятичными знаками и не является приблизительным.

+0

Спасибо, используя DECIMAL, правильно, я буду держать это в виду на будущее – ipronet

+0

@ipronet Добро пожаловать! – shadyyx

+0

@ipronet Также, пожалуйста, отметьте ответ, если это было полезно. Спасибо! – shadyyx

5

Точные числовые литералы (например, 1.15 во всех четырех из ваших примеров и 11.5 в вашем последнем два) являются encoded с использованием MySQL в DECIMAL типа.

В своих первых двух примерах, результат умножения field (типа DOUBLE) с такими литералами является еще одним DOUBLE, который хранится с использованием 8-byteIEEE Standard представления с плавающей точкой (т.е. binary64). Тем не менее, в этом представлении 13.225 кодирует, как 0x402A733333333333, чьи биты обозначают:

 
Sign   : 0b0 

Biased exponent: 0b10000000010 
       = 1026 (representation includes bias of +1023, therefore exp = 3) 

Significand : 0b[1.]1010011100110011001100110011001100110011001100110011 
       = [1.]6531249999999999555910790149937383830547332763671875 
        ^hidden bit, not stored in binary representation 

Это соответствует:

 
    (-1)^0 * 1.6531249999999999555910790149937383830547332763671875 * 2^3 
=   13.2249999999999996447286321199499070644378662109375000 

Таким образом, этот результат округления до двух знаков после запятой дает 13.22 не 13.23.

Выполнение умножения с двумя типами DECIMAL, такими как два литерала, используемые в последних двух примерах, приводит к другому DECIMAL.В этом представлении 13.225 кодирует в формате binary-coded decimal с точной точностью, и поэтому операция округления приводит к 13.23, как и ожидалось.

Как уже упоминалось в MySQL manual:

числа с плавающей точкой иногда вызывать путаницу, потому что они являются приблизительными и не сохраняются в виде точных значений. Значение с плавающей запятой, как указано в инструкции SQL, может быть не таким, как значение, представленное внутри. Попытки рассматривать значения с плавающей запятой как точные в сравнении могут привести к проблемам. Они также зависят от зависимостей платформы или реализации. Эти вопросы относятся к типам данных FLOAT и DOUBLE. Для столбцов DECIMAL MySQL выполняет операции с точностью до девяти десятичных цифр, что должно решить наиболее распространенные проблемы неточности.

Если вы после точной точности, DECIMAL может быть более подходящим для ваших требований. Если вам нужен диапазон/производительность DOUBLE, но все же вы хотите получить нужный результат округления, вы можете CONVERT результат умножения на DECIMAL перед округлением.

Смежные вопросы