Блин... Я на API-шках помешался. Про языковые функции и забыл. Спасибо, что напомнили. Щас _ecvt ковыряю. Вроде не большая и делает как раз то, что мне надо.
Как преобразовать действительное число в строку?24.06.07 16:07 Автор: Vedrus <Serokhvostov Anton> Статус: Member Отредактировано 24.06.07 16:16 Количество правок: 3
Здравствуйте, все. Такая проблема. Пишу функцию для перевода действительного числа в строку. Работать должна как можно быстрее. Пытался осуществить через команды сопроцессора.
Идея следующая. Загрузить число в сопроцессор командой fld, домножить его на что-то вроде 10^x, чтобы осталась только целая часть. После этого сохранить это число командой fist. Дальше всё просто.
Оценку числа нулей в 10^x я придумал (3*p/10, где p – двоичный порядок числа). Вот только сформировать это число не получается. С командой fscale справиться не получилось.
Может у кого пример подобного есть? Но в принципе, мне нужна помощь только для формирования числа 10^x в действительной форме, остальное всё уже сделаю сам.
Пишу на asm'е.
Можно ещё 2 вопроса по поводу?27.06.07 17:54 Автор: Vedrus <Serokhvostov Anton> Статус: Member
Можно ещё 2 вопроса по поводу?
1. Почему некоторые числа в сопроцессор загружаются неточно (например, 2.3 загружается как 2.2999999), и как от этого избавиться?
2. Почему, когда используешь команду fincstp без предшествующей ffree, то второе загружаемое после этого в сопроцессор не загружаются числа (говорит, что NAN - не число)? Или может быть это касяк в моём процессор (у меня Athlon 900)?
Потому что 2.3 нельзя представить в виде многочлена конечной...29.06.07 10:14 Автор: DPP <Dmitry P. Pimenov> Статус: The Elderman Отредактировано 29.06.07 10:16 Количество правок: 2
> Можно ещё 2 вопроса по поводу? > 1. Почему некоторые числа в сопроцессор загружаются неточно > (например, 2.3 загружается как 2.2999999), и как от этого > избавиться?
Потому что 2.3 нельзя представить в виде многочлена конечной длины ( x1*2^-1 + x2*2^-2 + ... + xn*2^-n ) * 2^e
Не каждое число можно представить в виде конечной десятичной дроби (1/3, например), так же не каждое десятичное дробное число можно представить в виде конечной двоичное дроби. То же число 1/3 легко представляется в троичной системе счисления как 0.1, а число 1/2 в троичной системе не представляется, хотя спокойно представляется в десятичной и двоичной.
Как с этим бороться - да просто округлять. Если 2.3 округлять до одного знака после запятой, то и получится 2.3, а если до сотого знака, то все равно получится 2.3. Бывают обратные ситуации, когда получается 2.30000000000000001.
С флоатом бывает много казусов. Например не следует использовать сравнения на "равно". Причем при отладочной печати далекие младшие разряды могут быть и не напечатаны. Если использовать флоат малой точности, то цикл от нуля до милиарда с шагом 0.001 превращается в бесконечный.
> 2. Почему, когда используешь команду > fincstp без предшествующей > ffree, то второе загружаемое после этого > в сопроцессор не загружаются числа (говорит, что > NAN - не число)? Или может быть это > касяк в моём процессор (у меня Athlon 900)?
Это не по адресу вопрос. Надо глубоко в документацию лезть. Могу сказать, что это не косяк. Можно проверить на другом проце. В противном случае было бы много шума про такого рода баг.
Дело в том, что действительное число может быть 32, 64 и 80...25.06.07 11:39 Автор: DPP <Dmitry P. Pimenov> Статус: The Elderman
> Здравствуйте, все. Такая проблема. Пишу функцию для > перевода действительного числа в строку. Работать > должна как можно быстрее. Пытался осуществить > через команды сопроцессора.
Дело в том, что действительное число может быть 32, 64 и 80 битным, которое состоит и мантиссы и экспоненты. Разрядность и того и другого не маленькая. Если придется сталкиваться с большими числами, то голупо отказываться от экспоненциальной формы. Для простой формы предложу такой алгоритм:
Заведем массивчик делителей 0.001, 0.01, 0.1, 1, 10, 100, 1000. Циклом пытаемся найти остаток от деления на каждый элемент массива, используя тот же ассемблер сопроцессора (FPREM) для нахождения каждой цифры. Не забываем вычитать из числа эту цифру, умноженную на соответствующий делитель. С отрицательностью боремся в начале. Точку ставим в нужном месте.
> Идея следующая. Загрузить число в сопроцессор командой > fld, домножить его на что-то вроде > 10^x, чтобы осталась только целая часть. > После этого сохранить это число командой > fist. Дальше всё просто.
Это лишнее.
> Оценку числа нулей в 10^x я придумал > (3*p/10, где p – двоичный порядок > числа). Вот только сформировать это число не получается. С > командой fscale справиться не > получилось.
Оценку разрядности лучше делать через логарифм.
> Может у кого пример подобного есть? Но в принципе, > мне нужна помощь только для формирования числа > 10^x в действительной форме, остальное всё уже > сделаю сам. > > Пишу на asm'е.
Формат действительных чисел я уже во сне видеть буду... С...25.06.07 14:55 Автор: Vedrus <Serokhvostov Anton> Статус: Member
Формат действительных чисел я уже во сне видеть буду... С массивчеком делителей напряженка будет: даже в double-формате диапазон +-10^308. Это получается порядка 600 таких делителей. Работать конечно быстро будет, но хотелось бы покороче :)
Можно без массивчика: Сначала вычисляем десятичный логарифм,...25.06.07 18:42 Автор: DPP <Dmitry P. Pimenov> Статус: The Elderman
> Формат действительных чисел я уже во сне видеть буду... С > массивчеком делителей напряженка будет: даже в > double-формате диапазон +-10^308. Это получается порядка > 600 таких делителей. Работать конечно быстро будет, но > хотелось бы покороче :) Можно без массивчика: Сначала вычисляем десятичный логарифм, потом округляем его, вычитаем x, где x - количество десятичных знаков, которые надо вывести (точность, грубо говоря) вычисляем 10 в степени этот округленный логарифм, ето и будет первый делитель. Другие делители вычисляются умножением на 10. Массив не нужен.
DDP, спасибо за совет. Вот сырой вариант результата:26.06.07 11:16 Автор: Vedrus <Serokhvostov Anton> Статус: Member Отредактировано 26.06.07 11:23 Количество правок: 4
DDP, спасибо за совет. Вот сырой вариант результата:
.386
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\kernel32.lib
.data
upr dw ?
flag db 0
p1 dd 0
ten dq 10.0 ; десятка для преобразования к целому
P dq ?
fig dq -100.02313e20
del dq 0 ; текущий делитель для выделения цифры
dig dd ? ; очередная цифра
string db ' .0000000000000000D-000', 0
pattern db ' .0000000000000000D-000', 0
.code
; возведение в степень x^y
pow proc x: DWORD, y: DWORD
mov eax, y
fld qword ptr [eax]
mov eax, x
fld qword ptr [eax]
ftst
fstsw ax
sahf
jnc m1 ; переход, если x >= 0
inc flag ; взведём флаг, если x < 0
fabs ;xm1: fxch
fyl2x
fst st(1)
fabs ;z; сравнимzс единицей
fld1
fcom
fstsw ax
sahf
jp exit ; операнды не сравнимы
jnc m2 ; еслиz< 1, то переход на m2
jz m3 ; еслиz= 1, то переход на m3
; еслиz> 1, то приводим к формуле z = z1+z2, где z1 - целое, z2 - дробное, и z2 < 1
xor ecx, ecx ; счётчик вычитаний
m12: inc cx
fsub st(1), st(0)
fcom
fstsw ax
sahf
jp exit ; операнды не сравнимы
jz m12
jnc m2 ; еслиz< 1, то переход на m2
jmp m12 ; еслиz> 1, то переход на m12
m3: mov p1, 1
jmp $+7
m2: mov p1, ecx
fxch
f2xm1
fadd ; компенсируем 1
fild p1 ; показатель степени для fscale
fld1
fscale
fxch
ffree st(0)
fincstp
fmul
; проверка на отрицательную степень
cmp flag, 1
jnz exit
fld1
fxch
fdiv
exit:
ret
pow endp
double2str proc fg: DWORD, buf: LPBYTE
;---------- Загрузить шаблон числа
invoke lstrcpy, buf, addr pattern
;---------- Определить и сохранить знак числа
mov edx, fg
mov eax, fg
mov eax, [eax]
and eax, 80000000h
jz positive
mov ecx, buf
mov byte ptr [ecx], '-'
;---------- Сделать из числа положительное
fld qword ptr [edx]
fabs
fstp qword ptr [edx]
positive:
;---------- Определить порядок числа
fld1
fld qword ptr [edx]
fyl2x
fldl2t
fdiv st(1), st(0)
ffree st(0)
fincstp
fstcw upr
and upr, 1111001111111111b
fldcw upr
frndint
fstp P ; порядок числа
;---------- Сформировать первый делитель
push offset ten
push offset P
call pow
fld P
fistp P
;---------- Определить и сохранить знак порядка
lea eax, P
mov eax, [eax]
cmp eax, 0
jl negative
mov ecx, buf
add ecx, 19
mov byte ptr [ecx], '+'
;---------- Сохранить сам порядок
negative:
lea eax, P
mov eax, [eax]
mov bl, 10
div bl
add ah, 30h
mov ecx, buf
add ecx, 20
mov byte ptr [ecx+2], ah
xor ah, ah
div bl
add ah, 30h
mov byte ptr [ecx+1], ah
add al, 30h
mov byte ptr [ecx], al
;---------- Вытащить и сохранить цифры
mov ecx, buf
add ecx, 2
nextDig:
fld qword ptr [edx]
fdiv st(0), st(1)
fstcw upr
or upr, 0000110000000000b
fldcw upr
frndint
fist dig
fmul st(0), st(1)
fld qword ptr [edx]
fxch st(1)
fsubp st(1), st(0)
fstp qword ptr [edx]
fld ten
fdivp st(1), st(0)
mov eax, dig
add eax, 30h
mov [ecx], al
inc ecx
cmp byte ptr [ecx], 'D'
jne nextDig
ret
double2str endp
start:
finit
push offset string
push offset fig
call double2str
invoke ExitProcess, 0
end start
(c) Vedrus
---
Возможно, через некоторое время оптимизированный вариант выложу. Если кто, что ещё посоветует по-поводу буду рад.
На здоровье! Визуально проверить это ниасилил, да и не к...26.06.07 17:02 Автор: DPP <Dmitry P. Pimenov> Статус: The Elderman Отредактировано 26.06.07 17:11 Количество правок: 7
> Возможно, через некоторое время оптимизированный вариант > выложу. Если кто, что ещё посоветует по-поводу буду рад.
На здоровье! Визуально проверить это ниасилил, да и не к чему. Сейчас поясню...
Во первых, что я влез. Я просто вспомнил про табличную оптимизацию. Если нужно 600 значений по 8 байт и прога от этого будет быстрее работать хотя бы процентов на 10, то нужно плюнуть на эти лишних пять килобайт и завести массив. Скорость будет однозначно выше, так как операции с возведением в степень или даже делением/умножением на сопроцессоре медленнее, чем обращение/адресация к элементу массива.
Причем чем больше массив, тем быстрее вычисления. Тригонометрия будет считаться быстрее если от таблицы с шагом 0.1 радиан перейти к таблице с шагом в 0.01 радиан раз в десять, а количество элементов таблицы увеличится с 16 до 160.
Еще со времен четвертого фортрана было несколько форматов вывода чисел с плавающей запятой. Обычная, экспоненциальная и компактная, то есть либо обычная, либо экспоненциальная в зависимости от самого числа (для очень больших и очень маленьких чисел экспоненциальная, а так - обычная). Обычная - удобочитаемая. Экспоненциальная - универсальная. Какая вам была нужна я до сих пор не понял, от этого тоже много чего зависит.
Так вот поясню: В зависимости от того что из условий более важное, алгоритм можно еще убыстрить. А именно, если требуется сделать все через жопу сопроцессор, то отлаживайте свой вариант, а если важна скорость преобразование и форма вывода только экспоненциальная, то предложу такое решение. Дла него опять же надо хорошо вспомнить школьный курс логарифмов.
Короче:
1) Вычисляем десятичный логарифм.
2) Находим разность между ним и 20.
3) Умножаем исходное число на 10 в степени разности (полученой во втором пункте).
4) Преобразуем это в целое длинное число, а затем его уже в цифровую форму через обычные несопроцессорные, а целочисленные операции. Это и будет то, что должно быть после точки в мантиссе.
5) Как вычислить экспоненту, сами догадаетесь. Преобразовать ее в цифровую форму тоже легко.
Всего в этом алгоритме будет пяток вычислительных медленный инструкций с плавающей запятой, несколько инструкций загрузки/выгрузки и преобразований к целым. Поразрядное получение цифр дробной части мантиссы будет не медленнее, чем несколько вычислений на сопроцессоре.
Идея этого всего в том, чтобы преобразовать исходное число в число в диапазоне 10^19<=X<10^20, умножив его на 10 в соответствующей степени, Это число легко преобразуется в целое и оно будет определять необходимые значащие цифры, а значение экспоненты, легко получается из десятичного логарифма исходного числа, приведением его к целому значению.
А как делают стандартные RTL функции?25.06.07 11:11 Автор: AlexD <Alexander> Статус: Member
Блин... Я на API-шках помешался. Про языковые функции и забыл. Спасибо, что напомнили. Щас _ecvt ковыряю. Вроде не большая и делает как раз то, что мне надо.
Скажу по секрету - апишные так же реализованы:)29.06.07 21:53 Автор: AlexD <Alexander> Статус: Member