Акругленне лікаў у C # прадуцыруюць занадта шмат знакаў пасля коскі

Я спрабую акругліць <�моцны> паплавок да 6 знакаў пасля коскі. Пераўтварэнне з падвойных плаваць, здаецца, у значнай ступені гэта зрабіць, аднак у гэтым працэсе я заўважыў некаторыя дзівацтвы. Усё, што я раблю, я, здаецца, у канчатковым выніку з фіктыўнымі дадатковыя дзесятковыя значэнні на канец, калі я канвертаваць назад у <�моцны> двайны пазней. Я не магу пазбегнуць ператварэння назад у два разы - гэта частка прыкладання я не магу змяніць, так што я проста спрабую зразумець, чаму мой код вырабляе дадатковыя дзесятковых разрадаў, якія першапачаткова не існавала.

напрыклад, Я пачынаю з наступным значэннем і пераўтварыць яго ў двайны:

float foo = 50.8467178;
double bar = Convert.ToDouble32(foo); 
// Sorry line above originally said ToInt32 which was a typo :(

..то "бар" будзе: <�моцны> 50,846717834472656 у адладчыка.

However:

Convert.ToDouble(50.8467178).ToString()

... вырабляе: <�моцны> 50.8467178 (у адладчыка). Гэта значыць, ён не мае дадатковыя значэння.

Чаму з'явіліся дадатковыя лічбы ў канцы? Як я магу спыніць гэта? І, нарэшце: Чаму выклік ToString() на любым з вышэйпералічанага, часта выхадных рознага ліку знакаў пасля коскі у параўнанні з тым, што адладчыкам паказвае?

1
Convert.ToInt32 (), каб дабрацца да дубля з якая плавае кропкай?
дададзена аўтар DonBoitnott, крыніца
Convert.ToInt32 (), каб дабрацца да дубля з якая плавае кропкай?
дададзена аўтар DonBoitnott, крыніца
спасылка @Matthew Уотсан тлумачыць, што вы робіце няправільна
дададзена аўтар Nahum, крыніца
спасылка @Matthew Уотсан тлумачыць, што вы робіце няправільна
дададзена аўтар Nahum, крыніца
@MatthewWatson: Калі ласка, не пакідайце гэтую спасылку толькі таму, што з якая плавае кропкай ўдзельнічае. Гэта тут недарэчна, таму што яна не тлумачыць паводзіны паведамляецца ў гэтым пытанні.
дададзена аўтар Eric Postpischil, крыніца
@MatthewWatson: Калі ласка, не пакідайце гэтую спасылку толькі таму, што з якая плавае кропкай ўдзельнічае. Гэта тут недарэчна, таму што яна не тлумачыць паводзіны паведамляецца ў гэтым пытанні.
дададзена аўтар Eric Postpischil, крыніца
@MatthewWatson: Калі ласка, не пакідайце гэтую спасылку толькі таму, што з якая плавае кропкай ўдзельнічае. Гэта тут недарэчна, таму што яна не тлумачыць паводзіны паведамляецца ў гэтым пытанні.
дададзена аўтар Eric Postpischil, крыніца
дададзена аўтар Matthew Watson, крыніца
дададзена аўтар Matthew Watson, крыніца
Вы кажаце, што пасля двайны бар = Convert.ToInt32 (Foo); «..то" бар "будзе: у адладчык 50,846717834472656.» Але, як вы пераўтварыць яго ў Int32, бар будзе 50,0 ?
дададзена аўтар Widor, крыніца
Вы кажаце, што пасля двайны бар = Convert.ToInt32 (Foo); «..то" бар "будзе: у адладчык 50,846717834472656.» Але, як вы пераўтварыць яго ў Int32, бар будзе 50,0 ?
дададзена аўтар Widor, крыніца

11 адказы

Вы павінны будзеце выкарыстоўваць, Math.Round <�код /> функцыі.

Math.Round(Convert.ToDouble(50.8467178)), 2);

Вы можаце ўзяць даведку з ГЭТАГА спасылкі.

Ці ж, як гэта:

String.Format("{0:0.00}", Convert.ToDouble(50.8467178).ToString()); //two places

Калі ласка, азнаёмцеся з раздзелам ГЭТАЙ спасылцы .

3
дададзена
глядзіце маё рэдагаванне
дададзена аўтар Freelancer, крыніца
У мяне няма ніякага кантролю над тым, дзе ён, нарэшце, пераўтворыцца ў радок. Гэта ўнутры класа XMLSerialiser.
дададзена аўтар NickG, крыніца
Гэта не тлумачыць паводзіны паведамляецца ў пытанні.
дададзена аўтар Eric Postpischil, крыніца
Я сапраўды не думаю, што ён павінен быць акругленне самага ліку; ён павінен апрацоўваць закруглення толькі пры пераўтварэнні яго ў радок.
дададзена аўтар Matthew Watson, крыніца

Вы павінны будзеце выкарыстоўваць, Math.Round <�код /> функцыі.

Math.Round(Convert.ToDouble(50.8467178)), 2);

Вы можаце ўзяць даведку з ГЭТАГА спасылкі.

Ці ж, як гэта:

String.Format("{0:0.00}", Convert.ToDouble(50.8467178).ToString()); //two places

Калі ласка, азнаёмцеся з раздзелам ГЭТАЙ спасылцы .

3
дададзена
глядзіце маё рэдагаванне
дададзена аўтар Freelancer, крыніца
У мяне няма ніякага кантролю над тым, дзе ён, нарэшце, пераўтворыцца ў радок. Гэта ўнутры класа XMLSerialiser.
дададзена аўтар NickG, крыніца
Гэта не тлумачыць паводзіны паведамляецца ў пытанні.
дададзена аўтар Eric Postpischil, крыніца
Я сапраўды не думаю, што ён павінен быць акругленне самага ліку; ён павінен апрацоўваць закруглення толькі пры пераўтварэнні яго ў радок.
дададзена аўтар Matthew Watson, крыніца

Вы павінны будзеце выкарыстоўваць, Math.Round <�код /> функцыі.

Math.Round(Convert.ToDouble(50.8467178)), 2);

Вы можаце ўзяць даведку з ГЭТАГА спасылкі.

Ці ж, як гэта:

String.Format("{0:0.00}", Convert.ToDouble(50.8467178).ToString()); //two places

Калі ласка, азнаёмцеся з раздзелам ГЭТАЙ спасылцы .

3
дададзена
глядзіце маё рэдагаванне
дададзена аўтар Freelancer, крыніца
У мяне няма ніякага кантролю над тым, дзе ён, нарэшце, пераўтворыцца ў радок. Гэта ўнутры класа XMLSerialiser.
дададзена аўтар NickG, крыніца
Гэта не тлумачыць паводзіны паведамляецца ў пытанні.
дададзена аўтар Eric Postpischil, крыніца
Я сапраўды не думаю, што ён павінен быць акругленне самага ліку; ён павінен апрацоўваць закруглення толькі пры пераўтварэнні яго ў радок.
дададзена аўтар Matthew Watson, крыніца

Прычына, па якой з'яўляецца дадатковыя знакі пасля коскі, верагодна, з-за невялікую страту дакладнасці пры пераходзе ад падвойнага паплаўка (пераўтварэння з якая плавае коскі ніколі не ідэальна).

Калі вам трэба акругліць лік, то пасля таго, як вы зрабілі пераўтварэнне вы можаце зрабіць:

result = Math.Round(result, 2);

Edit: Улічваючы каментар Эрыка ніжэй, вось некаторыя дадатковыя звесткі:

А паплаўка ў C# 32 біт з якая плавае кропкай ўяўленне колькасці а, у той час як двайны 64 біт. Не кожнае лік зусім прадстаўляльныя лік з якая плавае кропкай. Пры пераўтварэнні з 32 бітнай плавае кропкай на 64 біт у два разы ў вас ёсць 32 дадатковых бітаў для запаўнення інфармацыяй. Гэтая дадатковая інфармацыя заканчвае тым, што дадатковыя лічбы, якія вы бачыце:

float foo = 50.8467178f;
double bar = System.Convert.ToDouble(foo);
System.Console.WriteLine(bar);//prints 50.8467178344727 on my machine

Так як зыходны лік не зусім прадстаўляльныя з якая плавае кропкай, то выхад заканчвае тым, што злёгку розны лік з-за працэс пераўтварэння.

Калі вам неабходна падтрымліваць дакладнасць гэтага ліку вы павінны выкарыстоўваць дзесятковы значэння замест:

decimal foo = 50.8467178M;
double bar = System.Convert.ToDouble(foo);
System.Console.WriteLine(bar);//prints 50.8467178 on my machine

Калі вы не можаце выкарыстоўваць дзесятковую (напрыклад у паплаўка з'яўляецца выхадам бібліятэкі трэцяга боку або што-то), то акругленне падыход наступны лепшы варыянт:

float foo = 50.8467178f;
double bar = System.Convert.ToDouble(foo);
bar = System.Math.Round(bar, 7);
System.Console.WriteLine(bar);//prints 50.8467178 on my machine
1
дададзена
Існуе толькі адзін Int64 значэнне, якое адлюстроўваецца ў полі Int32 значэнне 2. У адрозненне ад гэтага, ёсць мільёны двайны значэнняў, якія адлюстроўваюць ў 0.1f . Стандарт IEEE кажа, што матэматыка павінна быць выканана так, як быццам гэта былі вылічаныя з дакладнымі значэннямі, а затым акругліць, але гэта не азначае, што 0.1f варта разглядаць як дакладнае колькасць, якое больш, чым ,100000000558793544769287109375. Хутчэй за ўсё, ён павінен ўяўляць сабой нешта, якое нічым не адрозніваецца ад любых лікавых значэнняў, якія будуць адны і тыя ж паплавок ўяўленне.
дададзена аўтар supercat, крыніца
@EricPostpischil: Нармальны прызначэнне значэнняў з якая плавае кропкай павінен стаяць у рэальных лікавых велічынь. Ні Single , ні Double сапраўды ўяўляе дакладную магутнасці з-двух фракцый. Калі гэта так, 0.1f ня варта абагульняць, але павінны быць напісаны як 0.100000001490116119384765625f . Ўзор біт, атрыманага 0.1f , аднак, на самай справе не ўяўляе, што дакладная колькасць, але замест таго, каб «тое, што гэта, верагодна, у межах 0,1 частак на мільён 0.1000000015», што робіць яго правільнае ўяўленне колькасць 1/10.
дададзена аўтар supercat, крыніца
@EricPostpischil: Хоць IEEE паказвае, што аперацыя х + у павінен атрымаць найлепшую з якая плавае коскі ўяўленне намінальным кошце плюс х намінальным кошце у, гэта не азначае, што х сапраўды «ўяўляе» сваю намінальны кошт. Намінальныя значэння 0.1f і (падвойныя) 0.1F могуць быць аднолькавымі, але сэнсавыя значэнні адрозніваюцца. Там няма падвойнага значэння якога сэнсавае значэнне супадае з 0.1f. На самай справе, я б пастуляваць, што пераўтварэнне паплаўка дубля звычайна змяняе сваё сэнсавае значэнне больш, чым робіць пераўтварэнне падвойнага паплаўка.
дададзена аўтар supercat, крыніца
Я б пастуляваць, што адліваная вар woggle = (Fnord) woozle павінен кінуць выключэнне, калі няма магчымае значэнне Fnord не будзе легітымнай ўяўленне woozle </код >. Такім чынам, калі кідок ня кідае выключэнне, альбо код адліўкі зламаны, альбо woggle трымае Fnord , які законна ўяўляе woozle . Той факт, што (з якая плавае кропкай) 0,1 і (паплавок) ,100000001 Доля ж бітавы шаблон мае на ўвазе мне, што гэты бітавы шаблон з'яўляецца законным паплавок прадстаўленне і 0,1 і ,100000001 .
дададзена аўтар supercat, крыніца
@supercat: Няма такога значэння не паказаны ў стандарце або ў стандартах мовы IEEE 754, з якімі я знаёмы. Пра тое, што аб'екты з якая плавае коскі ўяўляюць сабой інтэрвалы фальклор перадаецца apocryphally. Для таго, каб правільна выкарыстоўваць з якая плавае кропкай, выкарыстоўваць IEEE 754 або іншыя прыдатныя спецыфікацыі.
дададзена аўтар Eric Postpischil, крыніца
@supercat: (а) Паняцце «сэнсавага значэння» знаходзіцца за межамі мовы праграмавання і якія не маюць стаўленне да гэтага пытання. (Б) Па гэтаму стандарту, семантыка значэння 2 і 2l адрозніваюцца, таму цэлыя пераўтварэнні ніколі не дасканалы.
дададзена аўтар Eric Postpischil, крыніца
Гэты адказ з'яўляецца правільным, што частка праблемы з'яўляецца памылкай акруглення, выкліканае пераўтварэннем плаваць, але гэта з'яўляецца няпоўным, паколькі ён не тлумачыць, чаму адлюстроўваючы значэнне, атрыманае з больш вузкай дакладнасці вырабляе больш лічбаў, чым адлюстроўваючы значэнне, атрыманае з больш шырокай дакладнасці. Таксама няправільна сцвярджаць, што пераўтварэнні з якая плавае коскі ніколі не дасканалы. У IEEE-754, пераўтварэння з флоат да двайны з'яўляюцца без памылак, а таксама пераўтварэнні з двайны да паплаўка з'яўляюцца памылкі бясплатна, калі зыходнае значэнне прадстаўляльныя ў выглядзе флоат .
дададзена аўтар Eric Postpischil, крыніца

Прычына, па якой з'яўляецца дадатковыя знакі пасля коскі, верагодна, з-за невялікую страту дакладнасці пры пераходзе ад падвойнага паплаўка (пераўтварэння з якая плавае коскі ніколі не ідэальна).

Калі вам трэба акругліць лік, то пасля таго, як вы зрабілі пераўтварэнне вы можаце зрабіць:

result = Math.Round(result, 2);

Edit: Улічваючы каментар Эрыка ніжэй, вось некаторыя дадатковыя звесткі:

А паплаўка ў C# 32 біт з якая плавае кропкай ўяўленне колькасці а, у той час як двайны 64 біт. Не кожнае лік зусім прадстаўляльныя лік з якая плавае кропкай. Пры пераўтварэнні з 32 бітнай плавае кропкай на 64 біт у два разы ў вас ёсць 32 дадатковых бітаў для запаўнення інфармацыяй. Гэтая дадатковая інфармацыя заканчвае тым, што дадатковыя лічбы, якія вы бачыце:

float foo = 50.8467178f;
double bar = System.Convert.ToDouble(foo);
System.Console.WriteLine(bar);//prints 50.8467178344727 on my machine

Так як зыходны лік не зусім прадстаўляльныя з якая плавае кропкай, то выхад заканчвае тым, што злёгку розны лік з-за працэс пераўтварэння.

Калі вам неабходна падтрымліваць дакладнасць гэтага ліку вы павінны выкарыстоўваць дзесятковы значэння замест:

decimal foo = 50.8467178M;
double bar = System.Convert.ToDouble(foo);
System.Console.WriteLine(bar);//prints 50.8467178 on my machine

Калі вы не можаце выкарыстоўваць дзесятковую (напрыклад у паплаўка з'яўляецца выхадам бібліятэкі трэцяга боку або што-то), то акругленне падыход наступны лепшы варыянт:

float foo = 50.8467178f;
double bar = System.Convert.ToDouble(foo);
bar = System.Math.Round(bar, 7);
System.Console.WriteLine(bar);//prints 50.8467178 on my machine
1
дададзена
Існуе толькі адзін Int64 значэнне, якое адлюстроўваецца ў полі Int32 значэнне 2. У адрозненне ад гэтага, ёсць мільёны двайны значэнняў, якія адлюстроўваюць ў 0.1f . Стандарт IEEE кажа, што матэматыка павінна быць выканана так, як быццам гэта былі вылічаныя з дакладнымі значэннямі, а затым акругліць, але гэта не азначае, што 0.1f варта разглядаць як дакладнае колькасць, якое больш, чым ,100000000558793544769287109375. Хутчэй за ўсё, ён павінен ўяўляць сабой нешта, якое нічым не адрозніваецца ад любых лікавых значэнняў, якія будуць адны і тыя ж паплавок ўяўленне.
дададзена аўтар supercat, крыніца
@EricPostpischil: Нармальны прызначэнне значэнняў з якая плавае кропкай павінен стаяць у рэальных лікавых велічынь. Ні Single , ні Double сапраўды ўяўляе дакладную магутнасці з-двух фракцый. Калі гэта так, 0.1f ня варта абагульняць, але павінны быць напісаны як 0.100000001490116119384765625f . Ўзор біт, атрыманага 0.1f , аднак, на самай справе не ўяўляе, што дакладная колькасць, але замест таго, каб «тое, што гэта, верагодна, у межах 0,1 частак на мільён 0.1000000015», што робіць яго правільнае ўяўленне колькасць 1/10.
дададзена аўтар supercat, крыніца
@EricPostpischil: Хоць IEEE паказвае, што аперацыя х + у павінен атрымаць найлепшую з якая плавае коскі ўяўленне намінальным кошце плюс х намінальным кошце у, гэта не азначае, што х сапраўды «ўяўляе» сваю намінальны кошт. Намінальныя значэння 0.1f і (падвойныя) 0.1F могуць быць аднолькавымі, але сэнсавыя значэнні адрозніваюцца. Там няма падвойнага значэння якога сэнсавае значэнне супадае з 0.1f. На самай справе, я б пастуляваць, што пераўтварэнне паплаўка дубля звычайна змяняе сваё сэнсавае значэнне больш, чым робіць пераўтварэнне падвойнага паплаўка.
дададзена аўтар supercat, крыніца
Я б пастуляваць, што адліваная вар woggle = (Fnord) woozle павінен кінуць выключэнне, калі няма магчымае значэнне Fnord не будзе легітымнай ўяўленне woozle </код >. Такім чынам, калі кідок ня кідае выключэнне, альбо код адліўкі зламаны, альбо woggle трымае Fnord , які законна ўяўляе woozle . Той факт, што (з якая плавае кропкай) 0,1 і (паплавок) ,100000001 Доля ж бітавы шаблон мае на ўвазе мне, што гэты бітавы шаблон з'яўляецца законным паплавок прадстаўленне і 0,1 і ,100000001 .
дададзена аўтар supercat, крыніца
@supercat: Няма такога значэння не паказаны ў стандарце або ў стандартах мовы IEEE 754, з якімі я знаёмы. Пра тое, што аб'екты з якая плавае коскі ўяўляюць сабой інтэрвалы фальклор перадаецца apocryphally. Для таго, каб правільна выкарыстоўваць з якая плавае кропкай, выкарыстоўваць IEEE 754 або іншыя прыдатныя спецыфікацыі.
дададзена аўтар Eric Postpischil, крыніца
@supercat: (а) Паняцце «сэнсавага значэння» знаходзіцца за межамі мовы праграмавання і якія не маюць стаўленне да гэтага пытання. (Б) Па гэтаму стандарту, семантыка значэння 2 і 2l адрозніваюцца, таму цэлыя пераўтварэнні ніколі не дасканалы.
дададзена аўтар Eric Postpischil, крыніца
Гэты адказ з'яўляецца правільным, што частка праблемы з'яўляецца памылкай акруглення, выкліканае пераўтварэннем плаваць, але гэта з'яўляецца няпоўным, паколькі ён не тлумачыць, чаму адлюстроўваючы значэнне, атрыманае з больш вузкай дакладнасці вырабляе больш лічбаў, чым адлюстроўваючы значэнне, атрыманае з больш шырокай дакладнасці. Таксама няправільна сцвярджаць, што пераўтварэнні з якая плавае коскі ніколі не дасканалы. У IEEE-754, пераўтварэння з флоат да двайны з'яўляюцца без памылак, а таксама пераўтварэнні з двайны да паплаўка з'яўляюцца памылкі бясплатна, калі зыходнае значэнне прадстаўляльныя ў выглядзе флоат .
дададзена аўтар Eric Postpischil, крыніца

Прычына, па якой з'яўляецца дадатковыя знакі пасля коскі, верагодна, з-за невялікую страту дакладнасці пры пераходзе ад падвойнага паплаўка (пераўтварэння з якая плавае коскі ніколі не ідэальна).

Калі вам трэба акругліць лік, то пасля таго, як вы зрабілі пераўтварэнне вы можаце зрабіць:

result = Math.Round(result, 2);

Edit: Улічваючы каментар Эрыка ніжэй, вось некаторыя дадатковыя звесткі:

А паплаўка ў C# 32 біт з якая плавае кропкай ўяўленне колькасці а, у той час як двайны 64 біт. Не кожнае лік зусім прадстаўляльныя лік з якая плавае кропкай. Пры пераўтварэнні з 32 бітнай плавае кропкай на 64 біт у два разы ў вас ёсць 32 дадатковых бітаў для запаўнення інфармацыяй. Гэтая дадатковая інфармацыя заканчвае тым, што дадатковыя лічбы, якія вы бачыце:

float foo = 50.8467178f;
double bar = System.Convert.ToDouble(foo);
System.Console.WriteLine(bar);//prints 50.8467178344727 on my machine

Так як зыходны лік не зусім прадстаўляльныя з якая плавае кропкай, то выхад заканчвае тым, што злёгку розны лік з-за працэс пераўтварэння.

Калі вам неабходна падтрымліваць дакладнасць гэтага ліку вы павінны выкарыстоўваць дзесятковы значэння замест:

decimal foo = 50.8467178M;
double bar = System.Convert.ToDouble(foo);
System.Console.WriteLine(bar);//prints 50.8467178 on my machine

Калі вы не можаце выкарыстоўваць дзесятковую (напрыклад у паплаўка з'яўляецца выхадам бібліятэкі трэцяга боку або што-то), то акругленне падыход наступны лепшы варыянт:

float foo = 50.8467178f;
double bar = System.Convert.ToDouble(foo);
bar = System.Math.Round(bar, 7);
System.Console.WriteLine(bar);//prints 50.8467178 on my machine
1
дададзена
Існуе толькі адзін Int64 значэнне, якое адлюстроўваецца ў полі Int32 значэнне 2. У адрозненне ад гэтага, ёсць мільёны двайны значэнняў, якія адлюстроўваюць ў 0.1f . Стандарт IEEE кажа, што матэматыка павінна быць выканана так, як быццам гэта былі вылічаныя з дакладнымі значэннямі, а затым акругліць, але гэта не азначае, што 0.1f варта разглядаць як дакладнае колькасць, якое больш, чым ,100000000558793544769287109375. Хутчэй за ўсё, ён павінен ўяўляць сабой нешта, якое нічым не адрозніваецца ад любых лікавых значэнняў, якія будуць адны і тыя ж паплавок ўяўленне.
дададзена аўтар supercat, крыніца
@EricPostpischil: Нармальны прызначэнне значэнняў з якая плавае кропкай павінен стаяць у рэальных лікавых велічынь. Ні Single , ні Double сапраўды ўяўляе дакладную магутнасці з-двух фракцый. Калі гэта так, 0.1f ня варта абагульняць, але павінны быць напісаны як 0.100000001490116119384765625f . Ўзор біт, атрыманага 0.1f , аднак, на самай справе не ўяўляе, што дакладная колькасць, але замест таго, каб «тое, што гэта, верагодна, у межах 0,1 частак на мільён 0.1000000015», што робіць яго правільнае ўяўленне колькасць 1/10.
дададзена аўтар supercat, крыніца
@EricPostpischil: Хоць IEEE паказвае, што аперацыя х + у павінен атрымаць найлепшую з якая плавае коскі ўяўленне намінальным кошце плюс х намінальным кошце у, гэта не азначае, што х сапраўды «ўяўляе» сваю намінальны кошт. Намінальныя значэння 0.1f і (падвойныя) 0.1F могуць быць аднолькавымі, але сэнсавыя значэнні адрозніваюцца. Там няма падвойнага значэння якога сэнсавае значэнне супадае з 0.1f. На самай справе, я б пастуляваць, што пераўтварэнне паплаўка дубля звычайна змяняе сваё сэнсавае значэнне больш, чым робіць пераўтварэнне падвойнага паплаўка.
дададзена аўтар supercat, крыніца
Я б пастуляваць, што адліваная вар woggle = (Fnord) woozle павінен кінуць выключэнне, калі няма магчымае значэнне Fnord не будзе легітымнай ўяўленне woozle </код >. Такім чынам, калі кідок ня кідае выключэнне, альбо код адліўкі зламаны, альбо woggle трымае Fnord , які законна ўяўляе woozle . Той факт, што (з якая плавае кропкай) 0,1 і (паплавок) ,100000001 Доля ж бітавы шаблон мае на ўвазе мне, што гэты бітавы шаблон з'яўляецца законным паплавок прадстаўленне і 0,1 і ,100000001 .
дададзена аўтар supercat, крыніца
@supercat: Няма такога значэння не паказаны ў стандарце або ў стандартах мовы IEEE 754, з якімі я знаёмы. Пра тое, што аб'екты з якая плавае коскі ўяўляюць сабой інтэрвалы фальклор перадаецца apocryphally. Для таго, каб правільна выкарыстоўваць з якая плавае кропкай, выкарыстоўваць IEEE 754 або іншыя прыдатныя спецыфікацыі.
дададзена аўтар Eric Postpischil, крыніца
@supercat: (а) Паняцце «сэнсавага значэння» знаходзіцца за межамі мовы праграмавання і якія не маюць стаўленне да гэтага пытання. (Б) Па гэтаму стандарту, семантыка значэння 2 і 2l адрозніваюцца, таму цэлыя пераўтварэнні ніколі не дасканалы.
дададзена аўтар Eric Postpischil, крыніца
Гэты адказ з'яўляецца правільным, што частка праблемы з'яўляецца памылкай акруглення, выкліканае пераўтварэннем плаваць, але гэта з'яўляецца няпоўным, паколькі ён не тлумачыць, чаму адлюстроўваючы значэнне, атрыманае з больш вузкай дакладнасці вырабляе больш лічбаў, чым адлюстроўваючы значэнне, атрыманае з больш шырокай дакладнасці. Таксама няправільна сцвярджаць, што пераўтварэнні з якая плавае коскі ніколі не дасканалы. У IEEE-754, пераўтварэння з флоат да двайны з'яўляюцца без памылак, а таксама пераўтварэнні з двайны да паплаўка з'яўляюцца памылкі бясплатна, калі зыходнае значэнне прадстаўляльныя ў выглядзе флоат .
дададзена аўтар Eric Postpischil, крыніца

Рабіць гэта:

Convert.ToDouble(50.8467178).ToString()

Is the same as Рабіць гэта:

(50.8467178).ToString();

Так як няяўнай тып нумара з'яўляецца двайны , і пераўтварэнне нічога не робіць.

Але калі вы пачынаеце з нумарам як флоат , пераўтварэнне робіць сілу, і вядома, што двайны блытае шмат разоў (гэты выпадак, а таксама пры параўнанні значэння, і г.д ...).

Я хацеў бы прапанаваць, выкарыстоўваючы дзесятковую , прамыя злепкі і Роўна замест == .

1
дададзена

Рабіць гэта:

Convert.ToDouble(50.8467178).ToString()

Is the same as Рабіць гэта:

(50.8467178).ToString();

Так як няяўнай тып нумара з'яўляецца двайны , і пераўтварэнне нічога не робіць.

Але калі вы пачынаеце з нумарам як флоат , пераўтварэнне робіць сілу, і вядома, што двайны блытае шмат разоў (гэты выпадак, а таксама пры параўнанні значэння, і г.д ...).

Я хацеў бы прапанаваць, выкарыстоўваючы дзесятковую , прамыя злепкі і Роўна замест == .

1
дададзена

Рабіць гэта:

Convert.ToDouble(50.8467178).ToString()

Is the same as Рабіць гэта:

(50.8467178).ToString();

Так як няяўнай тып нумара з'яўляецца двайны , і пераўтварэнне нічога не робіць.

Але калі вы пачынаеце з нумарам як флоат , пераўтварэнне робіць сілу, і вядома, што двайны блытае шмат разоў (гэты выпадак, а таксама пры параўнанні значэння, і г.д ...).

Я хацеў бы прапанаваць, выкарыстоўваючы дзесятковую , прамыя злепкі і Роўна замест == .

1
дададзена

Літаральнае 50.8467178 з'яўляецца двайны па змаўчанні, таму ён няяўна пераўтворыцца ў лік з якая плавае кропкай, калі asigned да Foo. І таму, што паплавок мае меншы Percision гэта круглявае і не так Acurate. Вы павінны выкарыстоўваць 50.8467178f калі вы Asign да Foo, таму што гэта паплавок літаральна. У другім прыкладзе вы конвертируете двайны літаральным удвая, так што няма ніякіх зменаў, і выводзіць яго як ёсць.

1
дададзена
Літаральнае быў толькі маё пытанне .. Я не выкарыстоўваць литералы ў маім кодзе, але дзякуй за ўказанне на гэта.
дададзена аўтар NickG, крыніца
Гэта не тлумачыць паводзіны паведамляецца ў пытанні.
дададзена аўтар Eric Postpischil, крыніца

Там на самай справе некалькі тонка розныя рэчы, якія адбываюцца ў гэтым прыкладзе:

<�Р> Я спрабую акругліць паплавок ўніз да 6 знакаў пасля коскі. Пераўтварэнне з двайны плаваць, здаецца, у значнай ступені гэта зрабіць

Калі вам трэба акругліць, выкарыстоўвайце Рулонны метад . Пераўтварэнне можа прывесці да страты дакладнасці, і з якая плавае кропкай тыпу не бясконца дакладныя, каб пачаць з.

<�Р> Чаму з'явіліся дадатковыя лічбы ў канцы?

Ўнутры плавае і парны прадстаўленыя ў двайковай сістэме згодна IEEE 754 стандарту. Ваш нумар, 50.8467178 не можа быць сапраўды прадстаўлена ў двайковым выглядзе, гэтак жа, як 1/3 не можа быць дакладна прадстаўлены ў дзесятковай сістэме злічэння (без паўтарэння лічбаў). У адпаведнасці са стандартам, лік як паплавок захоўваецца ў выглядзе 0x424B6304 . Гэта дакладнае да 24 біт, або 6-9 дзесятковых лічбаў. Цяпер, інтэрпрэтуючы гэта лік у адпаведнасці са стандартам, мы атрымліваем:

0x424B6304
= 1.58895993232727 * 2^5
= 50.84671783447264

Дысплей, няхай гэта будзе адладчык або выклік ToString() метаду, досыць разумны, каб ведаць, што толькі першыя 6-9 лічбаў мелі значэнне (што адпавядае стандарту IEEE). Прымаючы максімум 9 лічбаў, паплавок будзе адлюстроўвацца як 50.8467178 .

Аднак, калі гэты лік пераўтворыцца ў два разы, гэта двайковае значэнне <�моцны> не мяняецца. Яе ўнутраны шаблон трохі упэўнены, робіць, але двайковае лік па-ранейшаму інтэрпрэтуецца як 1,58895993232727 * 2 ^ 5 . Праблема заключаецца ў тым, што двайнікі з'яўляюцца дакладнымі да 53 біт, або 15-17 дзесятковых лічбаў. Так што цяпер, калі адлюстровываюцца, 15-17 лічбы паказаны, а не першапачатковыя 6-9. Такім чынам, яго не тое, што ёсць дадатковыя лічбы, калі з яго робяць двайны, ужо ёсць лічбы .

Цяпер, калі няма ніякага прамежкавага пераўтварэння з якая плавае кропкай, 50,8467178 можа быць больш дакладна прадстаўлены з выкарыстаннем двайны. Гэта захоўваецца ў выглядзе 0x40496C613FB5F875 . Цяпер я не зрабіў матэматыку на тым, што адзін яшчэ, але з дапамогай гэтай працэдуры, мы атрымаем нешта накшталт 50.84671780000000000000000000023155 . Улічваючы толькі першыя лічбы 15-17, прыводзіць да 50.8467178 відаць на дысплеі (са значным 0s апушчаныя).

<�Р> Як я магу спыніць гэта?

Ня круглае ліццё на паплаўкі, яны маюць толькі 6-9 дакладных лічбаў. Тып дзесятковага звычайна падыходзіць для калі дзесятковая дакладнасць неабходная, але так як вы не можаце змяніць гэтую частку кода, выкарыстоўваючы Round метад павінен быць дастаткова дакладным для вашага прыкладання (да таго часу, пакуль вы застаяцеся пад 1000000000).

<�Р> Чаму выклік ToString() на любым з вышэйпералічанага, часта выхадных рознага ліку знакаў пасля коскі у параўнанні з тым, што адладчык паказвае?

Я абыходзячы вакол гэтага пытання, каб паспрабаваць захаваць рэчы простымі і паслядоўнымі. Я даваў дыяпазон для дакладнасці ў дзесятковых лічбах: 6-9 для паплаўкоў, 15-17 для дубляў. Прымаючы двайнікоў, напрыклад, ToString() Метад па змаўчанні вяртае радок з 15 дзесятковых лічбаў. Можна, аднак, прымусіць яго вярнуць 17 дзесятковых лічбаў з дапамогай выкліку ToString ( "G17") (дакумент) , але не было б ніякай гарантыі, гэтыя дзве лічбы з'яўляюцца значнымі. Я падазраю, што адладчык выклікае гэтую версію для свайго дысплея, таму яна адрозніваецца ад ToString() .


Further reading: IEEE Arithmetic, by Oracle. It's quite technical though.

0
дададзена