如何使用Irvine32 WriteFloat(而不是printf)显示四舍五入为.001的浮点数 [英] How to display floating-point rounded to .001 with Irvine32 WriteFloat, not printf
问题描述
我对汇编语言非常陌生,所以请耐心等待...
I am very new to assembly language so bear with me...
我有一个四舍五入到最接近的.001的浮点,但仍显示为+ 1.6670000E + 000.我希望它显示为1.667.
I have a floating-point that I have rounded to the nearest .001, but still displays as something like +1.6670000E+000. I would like it to display simply as 1.667.
这是我的数字除法和四舍五入代码:
Here is my code for dividing and rounding the numbers:
fild num_a
fidiv num_b
fimul thousand
frndint
fidiv thousand
fst a_div_b
这是我显示浮动的代码:
And here is my code for displaying the float:
mov eax, num_a
call WriteDec
mov edx, OFFSET divide
call WriteString
mov eax, num_b
call WriteDec
mov edx, OFFSET equals
call WriteString
fld a_div_b
call WriteFloat
call CrLf
我在线上看了很多,关于如何取整数字有很多答案,但是它们仍然全部以扩展格式显示.我正在使用irvine32库.
I looked a lot online and there were many answers on how to round numbers, but they still all displayed in the extended format. I am using the irvine32 library.
推荐答案
对注释中讨论的方法进行了更简单的修改:
An even a simpler modification to the approach discussed in comments:
乘以1000并用 fistp
将其转换为整数(默认四舍五入为最接近值),而不是四舍五入为整数值的 long double
使用 frndint
.
Multiply by 1000 and convert that to integer with fistp
(with the default rounding to nearest), instead of just rounding to an integer-valued long double
using frndint
.
该整数的低3个小数位是数字的小数部分.,即您现在有了十进制定点. div
乘以1000可得到商(整数部分)和余数(小数部分).打印两个部分,并在它们之间使用.
.
The low 3 decimal digits of that integer are the fractional part of your number. i.e. you now have decimal fixed-point. div
by 1000 gives you quotient (integer part) and remainder (fractional part). Print both parts with a .
between them.
You'll want to do manual int->string conversion (How do I print an integer in Assembly Level Programming without printf from the c library?) or otherwise print leading zeros in the fractional part. (So 2.062
doesn't turn into 2.62
)
这比在FP中分成整数和小数部分要容易得多,在FP中,整数和小数部分需要四舍五入为零,以确保您得到非负的小数部分.整数除法自然会截断为零,但是传统的x87 FP-> int转换只能使用默认的舍入模式.(除了SSE3 cvttsd2si
vs. cvtsd2si
This is easier than separating into integer and fractional parts in FP, which would require rounding with truncation toward zero to make sure you got a non-negative fractional part. Integer division naturally truncates towards zero, but legacy x87 FP->int conversion can only use the default rounding mode. (Except with SSE3 fisttp
.) SSE1/2 had XMM FP->int conversions with truncation or current rounding mode since they were introduced, like cvttsd2si
vs. cvtsd2si
缺点:对于较小的浮点输入,会溢出32位整数,因为单个32位整数必须保存 x * 1000
.
Downside: overflows a 32-bit integer for smaller float inputs, because a single 32-bit integer has to hold x * 1000
.
另一种方法是使用 x - (int)x
得到小数部分,并且只将 那个 小数部分乘以 1000.0
.这导致(int)x
与小数部分形成单独的整数,其中 x * 1000
仅作为浮点存在,而不存在 int32_t
The other way is to use x - (int)x
to get the fractional part and only multiplying that fractional part by 1000.0
. That leads to (int)x
in a separate integer from the the fractional part, with x*1000
only existing as floating point, not int32_t
.
有趣的事实:
AVX512DQ具有直接获取小数部分的指令: VREDUCESD xmm1,xmm2,xmm3/m64,imm8(和ss/ps/pd版本).这是 SSE4 roundsd /vrndscalesd在保留整数部分时将丢弃的部分.更加有趣:可以保留指定数量的分数位.但是,这些当然是二进制小数位,而不是小数位.
AVX512DQ has an instruction for getting the fractional part directly: VREDUCESD xmm1, xmm2, xmm3/m64, imm8 (and ss/ps/pd versions). It's the part that SSE4 roundsd / vrndscalesd would discard when keeping the integer part. Even more fun: can keep a specified number of fraction bits. But of course those are binary fraction bits, not decimal places.
如今,大多数x86 CPU具有SSE4.1,但只有Skylake-X高端台式机和现代Xeon具有AVX512DQ.:/和Ice Lake笔记本电脑.
Most x86 CPUs have SSE4.1 these days, but only Skylake-X high-end desktops and modern Xeons have AVX512DQ. :/ And Ice Lake laptops.
这篇关于如何使用Irvine32 WriteFloat(而不是printf)显示四舍五入为.001的浮点数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!