Как отобразить число с плавающей точкой, округленное до .001 с помощью Irvine32 WriteFloat, а не printf - PullRequest
0 голосов
/ 04 апреля 2020

Я очень плохо знаком с языком ассемблера, поэтому терпите меня ...

У меня есть плавающая точка, которую я округлил до ближайшего .001, но все равно отображается как что-то вроде + 1.6670000E + 000 , Я хотел бы, чтобы он отображался просто как 1.667.

Вот мой код для деления и округления чисел:

fild    num_a
fidiv   num_b
fimul   thousand
frndint
fidiv   thousand
fst     a_div_b

А вот мой код для отображения числа с плавающей запятой:

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.

1 Ответ

2 голосов
/ 04 апреля 2020

Еще более простая модификация подхода, обсуждаемого в комментариях:

Умножьте на 1000 и преобразуйте в целое число с fistp (с округлением по умолчанию до ближайшего) вместо просто округление до целочисленного значения long double с использованием frndint.

Младшие 3 десятичные цифры этого целого числа являются дробной частью вашего числа. т.е. у вас теперь есть десятичная дробь точка. div на 1000 дает вам частное (целая часть) и остаток (дробная часть). Печать обеих частей с . между ними.

Вы захотите выполнить ручное преобразование int-> string ( Как напечатать целое число в программировании на уровне сборки без printf из библиотеки c? ) или иначе печатать начальные нули в дробной части. (Таким образом, 2.062 не превращается в 2.62)


Это проще, чем разделение на целые и дробные части в FP, что потребует округления с усечением до нуля, чтобы убедиться, что вы получили неотрицательная дробная часть. Целочисленное деление естественным образом усекается до нуля, но унаследованное преобразование x87 FP-> int может использовать только режим округления по умолчанию. (За исключением SSE3 fisttp.) SSE1 / 2 имел XMM FP-> int преобразования с усечением или текущим режимом округления, так как они были введены, как cvttsd2si против cvtsd2si

Недостаток: переполняет 32-разрядное целое число для меньших чисел с плавающей запятой , поскольку одно 32-разрядное целое число должно содержать x * 1000.

Другой способ - использовать x - (int)x для получения дробной части и только умножение на дробной части на 1000.0. Это приводит к (int)x в отдельном целом числе от дробной части, с x*1000, существующим только с плавающей запятой, а не int32_t.


Интересный факт:

AVX512DQ есть инструкция для получения дробной части напрямую: VREDUCESD xmm1, xmm2, xmm3 / m64, imm8 (и версии ss / ps / pd). Это часть, которую SSE4 roundsd / vrndscalesd будет отбрасывать при сохранении целочисленной части. Еще интереснее: можно хранить указанное количество битов дроби. Но, конечно, это биты двоичной дроби, а не десятичные разряды.

В наши дни большинство процессоров x86 имеют SSE4.1, но AVX512DQ только у настольных компьютеров высшего класса Skylake-X и современных Xeon. : / И Ледяное озеро Ноутбуки.

...