Предотвратить округление / потерю точности - PullRequest
0 голосов
/ 09 мая 2018

Я пытаюсь сохранить некоторые числа в десятичной переменной, например, значение может быть:

Dim someDecimalVar as Decimal = 210.00483839999998

Выход составляет 210.0048384, но мне нужно точное значение. Почему числа теряют точность при сохранении в decimal / double и как мне предотвратить это?

Редактировать: Я получаю значение от объекта (пытаясь сделать их копию), который сохраняет их как двойные, как это возможно?

Ответы [ 2 ]

0 голосов
/ 10 мая 2018

Я быстро поигрался с числом в вашем вопросе, и если вы конвертируете двойное число в десятичное после явного присвоения его десятичной переменной, вы получите правильное число.

Е.Г.

Dim x As Double = 210.0048383999998
Dim someDecimalVar As Decimal = CDec(x)
0 голосов
/ 09 мая 2018

Без некоторого примера кода, демонстрирующего проблему для предоставления контекста, трудно сказать наверняка, что именно является вашей проблемой или как ее исправить. Однако, возможно, эта дополнительная информация поможет вам. Вы предоставили следующий пример:

Dim someDecimalVar as Decimal = 210.00483839999998

Значение 210.00483839999998 называется литералом (то есть жестко закодированным значением). Все литералы имеют определенный тип, даже если этот тип явно не указан. Если тип не указан, компилятор выводит тип на основе значения. Так, например, предполагается, что 1 - это Integer, 1.2 - это Double, а "Hello" - это String. Итак, в вашем примере кода числовой литерал интерпретируется компилятором как литерал Double, поскольку вы не указали. Другими словами, ваш код более или менее эквивалентен следующему:

Dim someDoubleVar as Double = 210.00483839999998
Dim someDecimalVar as Decimal = someDoubleVar

Итак, как вы можете указать тип литерала? В VB вы делаете это, добавляя символ type в качестве суффикса в конце вашего литерала. Итак, если вы хотите заставить компилятор интерпретировать это значение как Decimal, вам нужно добавить D в конец, например:

Dim someDecimalVar as Decimal = 210.00483839999998D

На самом деле, если вы не добавите символ типа, чтобы принудительно ввести его в Decimal, компилятор выдаст вам сообщение об ошибке:

Option Strict On запрещает неявные преобразования из «Double» в «Decimal».

Это означает, что компилятор знает, что преобразование из Double в Decimal может привести к потере некоторой точности, поэтому он отказывается выполнять преобразование автоматически; он хочет, чтобы вы вручную указали, что вы хотите выполнить преобразование, чтобы он знал, что вы знаете о последствиях. Это дополнительная проверка безопасности, которую компилятор делает для вас, чтобы убедиться, что вы случайно не делаете глупостей. Единственный способ получить это для компиляции без символа type - включить Option Strict Off, поэтому я должен предположить, что это то, что вы сделали. Я настоятельно рекомендую включить Option Strict On, поскольку это помогает улавливать проблемы такого рода.

Каждый раз, когда вы конвертируете между десятичными и двойными числами, значение может потерять некоторую точность при преобразовании (поскольку они сохраняют значение в памяти двумя различными, иногда несовместимыми, способами). Итак, соблюдайте:

Dim someDoubleVar As Double = 210.00483839999998R  ' Setting Double to Double retains precision
Dim someDoubleVar2 As Double = 210.00483839999998D  ' Setting Double to Decimal loses precision
Dim someDecimalVar As Decimal = 210.00483839999998D  ' Setting Decimal to Decimal retains precision
Dim someDecimalVar2 As Decimal = 210.00483839999998R  ' Setting Decimal to Double loses precision

Примечание: приведенный выше код будет компилироваться только с Option Strict Off. Я знаю ... Я просто сказал не делать этого, но я пытаюсь проиллюстрировать, почему ваш код делает то, что делает.

...