У меня есть single
который я считаю, что эквивалент C ++ float
в VBA в модуле книги Excel. В любом случае, первоначально назначенное мной значение (876,34497) округляется до 876,345 в «Немедленном окне» и «Подсказка» и всплывающей подсказке, когда я устанавливаю точку останова на VBA. Однако, если я пройду это Single
в C ++ DLL C ++ сообщает об этом как исходное значение 876.34497.
Итак, действительно ли оно хранится в памяти как исходное значение? Это какое-то ограничение отладчика? Не уверены, что здесь происходит. Трудно проверить, является ли то, что я передаю, тем, что я получаю на стороне C ++.
Я старался:
?CStr(test)
876.345
?CDbl(test)
876.344970703125
?CSng(test)
876.345
VBA не очень прост, поэтому на каком-то уровне он должен храниться в памяти как 876.34497. В противном случае я не думаю, CDbl
было бы правильно, как это.
Переменные VBA типа «single» хранятся как «32-битная аппаратная реализация IEEE 754 [-] 1985 [sic]». [увидеть: https://msdn.microsoft.com/en-us/library/ee177324.aspx].
На английском языке это означает, что «одинарные» числа точности преобразуются в двоичные, а затем усекаются до 4-байтовой (32-разрядной) последовательности. Точный процесс очень хорошо описан в Википедии под http://en.wikipedia.org/wiki/Single-precision_floating-point_format . В результате все числа одинарной точности выражаются как
(1) a 23 bit "fraction" between 0 and 1, *times*
(2) an 8-bit exponent which represents a multiplier between 2^(-127) and 2^128, *times*
(3) one more bit for positive or negative.
Процесс преобразования чисел в двоичные и обратно вызывает два типа ошибок округления:
(1) Значимые цифры — как вы заметили, существует ограничение на значащие цифры. 22-битное целое число может иметь только 8,388,607 уникальных значений. Другими словами, ни одно число не может быть выражено с точностью более +/- 0,000012%. Возвращаясь к науке о старшей школе, вы можете вспомнить, что это еще один способ сказать, что вы не можете рассчитывать на более чем шесть значащих цифр (ну, десятичные цифры, по крайней мере … конечно, у вас есть 22 значащие двоичные цифры). Таким образом, любое представление числа с более чем шестью значащими цифрами будет округлено. Однако, оно не будет округлено до ближайшей десятичной цифры … оно будет округлено до ближайшей двоичной цифры. Это часто приводит к неожиданным результатам (как у вас).
(2) Двоичное преобразование. Другой тип ошибок еще более пагубен. Есть некоторые числа со значительно менее чем шестью (десятичными) цифрами, которые будут округлены. Например, 1/5 в десятичном виде — это 0.2000000. Он никогда не округляется. Но то же самое число в двоичном виде — 0,00110011001100110011 …. повторяется вечно. (Эта последовательность эквивалентна 1/8 + 1/16 + 1/16 * (1/8 + 1/16) + 1/256 * (1/8 + 1/16) …) Если вы использовали произвольный число двоичных цифр для представления 0,20, а затем преобразовать его обратно в десятичную, вы никогда не получите точно 0,20. Например, если вы использовали восемь битов, у вас было бы 0,00110011 в двоичном формате, что:
0.12500000
0.06250000
0.00781250
+ 0.00390625
------------
0.19921875
Независимо от того, сколько двоичных цифр вы используете, вы никогда не получите точно 0,20, потому что 0,20 нельзя выразить как сумму степеней двух.
Это в двух словах объясняет, что происходит. Когда вы назначаете 876.34497 для «теста», он конвертируется внутри:
1 10001000 0110110001011000010011
136 5,969,427
Который (+1) * 2 ^ (136-127) * (5,969,427) / (2 ^ 23)
Excel автоматически усекает отображение вашего номера с одинарной точностью, чтобы показать только шесть значащих цифр, потому что он знает, что седьмая цифра может быть неправильной. Я не могу сказать вам, что это за номер, потому что мой Excel не отображает достаточно значащих цифр! Но вы поняли.
Когда вы приводите значение к двойной точности, оно использует всю двоичную строку, а затем добавляет в ноль еще 4 байта нулей. Теперь он позволяет отображать вдвое больше значащих цифр, потому что это двойная точность, но, как вы можете видеть, преобразование из 8 десятичных цифр в 23 двоичных цифры и последующее добавление еще одной длинной строки нулей привело к некоторым ошибкам. Не совсем ошибки, если вы понимаете, что он делает; просто артефакты. В конце концов, он делает именно то, что вы сказали, чтобы сделать … вы просто не знали, что вы говорите, что делать!