2014-10-21 3 views
1

Я написал код, но он дает неправильные результаты!Ошибка преобразования типа delphixe2! " дробные результаты?

Var 
    a,d:double; 
    c:currency; 
    b:integer; 
begin 
b:=10; 
c:=20.1; 
a:=30.1; 
d:=0; 

d:=a-b-c; 

Memo1.Lines.Add('a => '+FloatToStr(a)); 
Memo1.Lines.Add('b => '+FloatToStr(b)); 
Memo1.Lines.Add('c => '+FloatToStr(c)); 
Memo1.Lines.Add('d => '+FloatToStr(d)) 
end; 

Результат:

a => 30.1 
b => 10 
c => 20.1 
d => 1.4210854715202E-15 

Результаты должны быть НОЛЬ! Является ли эта ошибка преобразования типа компилятора?

Есть ли способ или другое? !!

ответ

5

Ну, первое, что можно сказать, это то, что вы играете здесь с огнем. Вы смешиваете Integer, Double и Currency в том же заявлении. Эта нечестивая смесь, честно говоря, просит неприятностей.

Кроме того, если вы еще не знаете, вы должны знать, что не все ваши значения являются представляемыми. Вот исходные значения:

  • a имеет тип Double, значение 30.1. Это значение не точно представлено в двоичной с плавающей запятой. Номер closest Double value: 30.10000000000000142108547152020037174224853515625.
  • b тип Integer, стоимость 10. Здесь нет сюрпризов.
  • c есть тип Currency, value 20.1. Это представлено как 64-битное целое число со значением 201000. Это тип данных с фиксированной точкой. Представление включает неявный сдвиг 10000.

Следующее ваше выражение, a - b - c. Он оценивается слева направо.

код под x86 выглядит следующим образом, с моими примечаниями:

 
// load b into the floating point unit, converting from 32 bit integer 
fild dword ptr [$00423ef0] 
// subtract b from a, as floating point 
fsubr qword ptr [$00423ed8] 
// multiply by 10000 to convert to currency 
fmul dword ptr [$0041c5e0] 
// load c into the floating point unit, converting from 64 bit integer 
fild qword ptr [$00423ee8] 
// subtract c from (b - a) 
fsubp st(1) 
// divide by 10000, that is convert the result to a floating point value 
fdiv dword ptr [$0041c5e0] 
// store this floating point value into d 
fstp qword ptr [$00423ee0] 

Значение, которое мы храним обратно в d не равен нуль. По сути, это потому, что (a - b)*10000 <> 20100. Теперь вы можете быть удивлены, узнав, что (a - b)*10000 <> 20100, но это естественное следствие того факта, что a не является точным изображением 30.1, как мы видели в первом пункте выше.

Я думаю, что мораль этой истории не должна смешивать двоичные и десятичные операнды в тех же выражениях. Или, по крайней мере, если вам нужно это сделать, будьте ясными и точными о том, как вы это делаете. Например.

Итак, если вы выполняете свои расчеты полностью в Currency, тогда вы получите ответ, который ожидаете. Это потому, что Currency является десятичным представлением и может хранить все эти значения и все промежуточные значения точно.

+0

«d» двойная переменная типа «30.1» фактически получает значение «30.10000000000000142108547152020037174224853515625« получение значения потрясающе! Логически я могу понять, что не нужно вводить преобразование? –

+0

Я не уверен, что вы имеете в виду. Вы просите больше помощи? –

+1

Я думаю, что OP может захотеть взглянуть на [Что каждый компьютерный ученый должен знать о арифметике с плавающей точкой] (http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html). –

0
Var 
     a,d:double; 
     c:double; 
     b:double; 
    begin 
    b:=10; 
    c:=20.1; 
    a:=30.1; 
    d:=0; 

    d:=a-b-c; 

    Memo1.Lines.Add('a => '+FloatToStr(a)); 
    Memo1.Lines.Add('b => '+FloatToStr(b)); 
    Memo1.Lines.Add('c => '+FloatToStr(c)); 
    Memo1.Lines.Add('d => '+FloatToStr(d)) 
    end; 

Результат:

a => 30.1 
b => 10 
c => 20.1 
d => 0 

Проблема улучшения переменной того же типа !!!

Тип «Двойной» при назначении значения разных типов (валюты), если мы используем эту ошибку.