Почему CLng дает разные результаты? - PullRequest
9 голосов
/ 14 февраля 2011

Вот небольшая жемчужина прямо из моего VBE (MS Excel 2007 VBA):

?clng(150*0.85)
 127 
x = 150*0.85
?clng(x)
 128 

Кто-нибудь может объяснить это поведение? ИМХО, первое выражение должно дать 128 (.5 округлено до ближайшего четного), или, по крайней мере, оба результата должны быть равны.

Ответы [ 5 ]

11 голосов
/ 14 февраля 2011

Я думаю, что wqw прав, но я приведу подробности.

В утверждении clng(150 * 0.85), 150 * 0.85 рассчитывается с расширенной точностью:

150 = 1.001011 x 2^7

0.85 с двойной точностью =

1.1011001100110011001100110011001100110011001100110011 x 2^-1

Умножьте их вручную, и вы получите

1.1111110111111111111111111111111111111111111111111111110001 x 2^6 =
127.4999999999999966693309261245303787291049957275390625

Это 59 бит, что удобно для расширенной точности.Это меньше чем 127.5, поэтому округляется в меньшую сторону.

В операторе x = 150 * 0.85 это 59-битное значение округляется до 53 бит, что дает

1.1111111 x 2^6 = 1111111.1 = 127.5

Таким образом, оно округляется в соответствии сот половины до вечера.

(см. мою статью http://www.exploringbinary.com/when-doubles-dont-behave-like-doubles/ для получения дополнительной информации.)

2 голосов
/ 14 февраля 2011

Ах, одна из «забавных» вещей в VBA - это округление по CInt () и т. Д., Это то, что называется округлением банкиров. Округление банкиров - это то, где значения 0,5 округляются в большую или меньшую сторону в зависимости от того, является ли число четным числом, то есть 2,5 округления до 2, от 3,5 до 4 и т. Д.

Подробнее о округлении можно узнать здесь

http://www.consultdmw.com/rounding-numbers.htm

1 голос
/ 14 февраля 2011

Моя теория состоит в том, что VBA / VB6 использует x87 для вычислений с плавающей запятой, и это неявно преобразует двойные числа в более высокую точность, если 80 битов для промежуточных результатов.Таким образом, присвоение v или явное приведение с CDbl преобразует промежуточное 80-битное значение обратно в 64-битное, эффективно его округляя (или обрезая).

Вот некоторые обсуждения:

Расширенная (80-битная) двойная плавающая точка в x87, а не SSE2 - мы не пропустили это?

1 голос
/ 14 февраля 2011

Это немного догадка, но 0,85 не могут быть представлены в виде числа с плавающей запятой. Если он выключен на 0,0000000000001, он все равно может странным образом повлиять на округление.

Если вы используете CDec (.85), чтобы перевести его в десятичный режим, вы не получите этой странности. Это одна из многих причин, почему я не использую одинарный / двойной, где важна точность.

0 голосов
/ 14 февраля 2011

И то, что сказали Кевин и Джонатан, верно, но ответ Джонатана здесь более применим. Если бы вы имели дело с числами типа валюты вместо числа с плавающей запятой, то будет применяться правило округления Банкира.

...