Как преобразовать строки в плавания с идеальной точностью?

Я пытаюсь записать функцию на языке программирования D для замены вызовов к C strtold. (Объяснение: Для использования strtold от D необходимо преобразовать струны ре в струны до, который неэффективен. Кроме того, strtold не может быть выполнен во время компиляции.) я придумал реализацию, которая главным образом работает, но я, кажется, теряю некоторую точность в наименее значимых битах.

Код к интересной части алгоритма ниже, и я вижу, куда потеря точности поступает из, но я не знаю, как избавиться от него. (Я не учел много частей кода, которые не относились к основному алгоритму для сохранения людей, читающих.), Какой алгоритм строки к плаванию гарантирует, что результат будет максимально близок на числовой оси IEEE к значению, представленному строкой.

real currentPlace = 10.0L ^^ (pointPos - ePos + 1 + expon);

real ans = 0;
for(int index = ePos - 1; index > -1; index--) {
    if(str[index] == '.') {
        continue;
    }

    if(str[index] < '0' || str[index] > '9') {
        err();
    }

    auto digit = cast(int) str[index] - cast(int) '0';
    ans += digit * currentPlace;
    currentPlace *= 10;
}

return ans * sign;

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

assert(to!(real)("0.456") == 0.456L);

Действительно ли возможно, что ответы, производимые моей функцией, на самом деле более точны, чем представление, которое компилятор производит при парсинге литерала с плавающей точкой, но компилятор (который записан в C++) всегда соглашается точно с strtold, потому что это использует strtold внутренне для парсинга литералов с плавающей точкой?

10
задан Blorgbeard 26 March 2014 в 03:20
поделиться

5 ответов

Начните с накапливания цифр в качестве целочисленного значения, игнорируя десятичную точку и экспоненту. Вы все равно будете использовать аккумулятор с плавающей точкой, но у вас нет дробной части, это избегает потери точности из-за неспособности точно выражать числа плавающих точек. (Вы также можете игнорировать дробные цифры, которые выходят за пределы точных поплавков для представляют собой 8 цифр для 32 бита IEEE поплавки).

Вы можете использовать 64-битное целое число, чтобы накапливать цифры, если вы предпочитаете, но вы должны быть осторожны, чтобы игнорировать дополнительные цифры, которые привели бы переполнить, если вы сделаете. (Возможно, вам все равно придется учитывать эти цифры при определении показателя)

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

1
ответ дан 3 December 2019 в 23:50
поделиться

Вы создаете номер с плавающей запятой для каждой цифры, а затем добавляют эти номера вместе. Поскольку числа с плавающей запятой не точны, но округлые до определенного количества двоичных цифр, это включает в себя небольшие неприменения при хранении единых чисел и добавления их. Поэтому добавление номеров с плавающей точкой для одной цифры вместе может дать результат с небольшой ошибкой округления.

Примером будет 0,1 + 0,02 , который составляет не , равный 0,12 , если он представлен в виде номера плавающей точки. (Чтобы проверить, просто попробуйте сравнить их в вашем любимом языке программирования)

0
ответ дан 3 December 2019 в 23:50
поделиться

Клинджер и Стил и Уайт разработали точные алгоритмы для чтения и записи с плавающей запятой.

Здесь есть ретроспектива вместе с некоторыми ссылками на реализации.

Статья Дэвида Гэя , улучшающая работу Клингера, и реализация Гая на C великолепны. Я использовал их во встроенных системах, и полагаю, что dtoa Гэя нашел свое место во многих libc .

10
ответ дан 3 December 2019 в 23:50
поделиться

Честно говоря, это один из тех вещей, которые вам действительно не следует делать, если вы еще не знаете, как это делать. Он полон подводных камней, и даже если вам удастся сделать это правильно, он, вероятно, будет чрезвычайно медленным, если у вас нет опыта в анализе производительности низкоуровневых вычислений.

Тем не менее, если вы действительно полны решимости написать свою собственную реализацию, лучшим ориентиром для правильности является «Правильно округленные двоично-десятичные и десятично-двоичные преобразования» Дэвида Гэя ( версия постскриптума ). Вам также следует изучить его эталонные реализации (на языке C), которые доступны на Netlib .

2
ответ дан 3 December 2019 в 23:50
поделиться

Вы не можете хранить большинство поплавков с идеальной точностью в цифровом компьютере

0
ответ дан 3 December 2019 в 23:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: