gcc с двойной точностью печати - неправильный вывод

#include <stdio.h>
#include <wchar.h> 
int main()
{
    double f = 1717.1800000000001;
    wprintf(L"double      %.20G\n", f);
    return 0;
}

результаты (и ожидаемые ниже):

double      1717.1800000000000637
double      1717.1800000000001

Это в Ubuntu 11.10 x64 (но также и при компиляции для 32-битной версии).

Проблема, которую я пытаюсь решить, заключается в том, что в Windows он выводит число точно так же, как в коде, и мне нужно сделать низкоуровневое форматирование (swprintf), чтобы оно работало как в Windows, для проблем с переносимостью.


person queen3    schedule 29.02.2012    source источник
comment
1717.1800000000001 не может быть точно представлено как double, поэтому вы получите только близкое к нему значение в f. Значение, хранящееся в f, равно 1717,180000000000063664629124104976654052734375. Проблема в том, что теперь Windows выводит только 17 значащих цифр, хотя было запрошено 20 (это известная ошибка, насколько я знаю, она где-то в их базе данных ошибок). Если вы не можете ограничить длину поля разумным значением (например, 17), вам нужна оболочка для имитации этой ошибки.   -  person Gunther Piez    schedule 29.02.2012
comment
Это очень интересно знать, можете ли вы вспомнить точную ошибку? Это то, что мне нужно, чтобы Linux-версия работала как Windows, даже с ее ошибками.   -  person queen3    schedule 29.02.2012
comment
На самом деле я установил .17 вместо .20, и теперь linux/windows полностью совместимы. Если вы измените свой комментарий на ответ, я с радостью приму его. Но если вы сможете найти точную ссылку на ошибку, это будет еще полезнее.   -  person queen3    schedule 29.02.2012
comment
Я не помню номер ошибки, но я помню, что она была закрыта как WONTFIX. Так что, вероятно, вы можете положиться на него.   -  person Gunther Piez    schedule 29.02.2012
comment
При этом вы выполняете глупую задачу, если ожидаете, что результаты вычислений FP будут побитно равными для разных компиляторов и разных ОС. Помимо очевидных проблем, таких как оптимизация компилятора, изменяющая порядок операций FP и ошибки в MS libc, у вас есть различия, такие как glibc printf с использованием roundTiesToEven и MSVC AFAIK с использованием roundTiesToAway.   -  person janneb    schedule 29.02.2012
comment
Что ж, для IEEE double вы можете ожидать одинакового поведения на разных компиляторах и платформах, они стандартизированы. Для округления в libc printf я не уверен. Существует ли стандарт для печати дубликатов, соответствующих IEEE?   -  person Gunther Piez    schedule 29.02.2012
comment
@drhirsch: с более агрессивными флагами оптимизации компиляторы часто оптимизируют вещи так, что побитовые равные результаты больше не получаются. (например, a+b-a =› b). А для 32-битной x86, конечно, есть дополнительное удовольствие из-за проблемы с избыточной точностью x87, дающей вам разные результаты в зависимости от распределителя регистров. Кроме того, MSVC IIRC устанавливает точность x87 на 53 бита, тогда как GCC оставляет ее на уровне 64 бит. И затем, конечно, немногие, если вообще какие-либо библиотеки, используемые сегодня, гарантируют правильно округленные результаты, что снова дает источник ошибок в последних битах.   -  person janneb    schedule 29.02.2012
comment
@drhirsch: Wrt printf, IEEE 754 требует, чтобы двоично-десятичные преобразования правильно округлялись в соответствии с текущим режимом округления. Поскольку режим округления по умолчанию — roundTiesToEven, можно утверждать, что поведение glibc является правильным.   -  person janneb    schedule 29.02.2012
comment
Я получил точное совпадение между Windows и Linux, когда установил точность на 17, поэтому (на данный момент) меня не волнуют возможные различия.   -  person queen3    schedule 29.02.2012


Ответы (4)


1717.1800000000001 не может быть точно представлено как double, поэтому вы получите только близкое к нему значение в f. Значение, хранящееся в f, равно 1717,180000000000063664629124104976654052734375.

Проблема теперь в том, что Windows выводит только 17 значащих цифр, хотя было запрошено 20 (это известная ошибка, насколько мне известно, она где-то в их базе данных ошибок).

Если вы не можете ограничить длину поля разумным значением (например, "%.17G"), вам нужна оболочка для имитации этой ошибки.

person Gunther Piez    schedule 29.02.2012
comment
Похоже, ошибка заключается в следующем: двоичные дроби" rel="nofollow noreferrer">connect.microsoft.com/VisualStudio/feedback/details/329278/ - person queen3; 29.02.2012
comment
@queen3: Да, это отчет об ошибке (я написал его). См. мою статью exploringbinary.com/ за кровавые подробности. - person Rick Regan; 29.02.2012
comment
@queen3: Хорошо замечено. Интересно узнать историю этой ошибки. - person Gunther Piez; 29.02.2012

Функция wprintf реализована стандартной библиотекой C, а не gcc.

Число 1717.1800000000001 состоит из 13 цифр после запятой. Однако он не имеет точного представления в 64-битном двоичном формате с плавающей запятой.

Формат "%.20G" требует вывода 20 значащих цифр, что является количеством цифр в 1717.1800000000000637. Следовательно, этот результат является ожидаемым. Либо стандартная библиотека C Windows обрабатывает формат по-разному, либо делает неправильное округление.

С другой стороны, вы можете потребовать определенное количество цифр после числа с плавающей запятой, используя формат "%f", например. "%.13f" округляет вывод ровно до 13 цифр после числа с плавающей запятой и печатает 1717.1800000000001, как и ожидалось.

person Maxim Egorushkin    schedule 29.02.2012

Ну, ваш ожидаемый вывод конфликтует с вашей строкой формата. «%.20G» означает, что вам нужно 20 значащих цифр, и это то, что вы получаете. Если вам нужны 17 значащих цифр, как и ожидаемый результат, используйте "%.17G".

person janneb    schedule 29.02.2012

double f = 1717.1800000000001;

Эта строка кода не может быть выполнена точно. Константа имеет точность 17 знаков после запятой, точность числа double — 15,9.

person user207421    schedule 29.02.2012
comment
Значение, данное ОП, не может быть точно представлено, это правда. Но остальная часть вашего ответа неверна, что можно довольно легко показать: строка f=1717.180000000000063664629124104976654052734375 будет выполнена точно, хотя цифр в ней еще больше. - person Gunther Piez; 29.02.2012
comment
Но это не та строка кода, которая не может быть выполнена точно, не так ли? И вам придется показать мне двоичное представление 1717.180000000000063664629124104976654052734375, потому что двойное число по-прежнему имеет только 53 бита, а этому значению нужно больше. - person user207421; 29.02.2012
comment
Нет, ему нужно ровно 53 бита. Просто попробуйте. Выполнить с помощью калькулятора, который может обрабатывать арифметические операции произвольной длины (например, gp) 15466982416256137216.0/2^53 - person Gunther Piez; 29.02.2012