Вопрос

В C89 функция floor() возвращает значение double.Гарантировано ли выполнение следующего?

double d = floor(3.0 + 0.5);
int x = (int) d;
assert(x == 3);

Меня беспокоит то, что результат floor может быть не совсем представим в IEEE 754.Таким образом, d получает что-то вроде 2.99999, а x в итоге равно 2.

Чтобы ответ на этот вопрос был утвердительным, все целые числа в диапазоне int должны быть точно представлены в виде double , и floor всегда должен возвращать это точно представленное значение.

Это было полезно?

Решение

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

Другие советы

Если результат floor() не совсем представим, каким, по-вашему, должно быть значение d ?Конечно, если у вас есть получил представление числа с плавающей запятой в переменной, тогда по определению оно точно представимо, не так ли?У тебя есть получил представление в d...

(Кроме того, ответ Мехрдада верен для 32-битных целых чисел.В компиляторе с 64-разрядным двойным и 64-битный int, у вас, конечно, больше проблем ...)

Редактировать:Возможно, вы имели в виду "теоретический результат floor(), т.е.наибольшее целое значение, меньшее или равное аргументу, может быть не представлено в виде int".Это, безусловно, так.Простой способ показать это для системы, где int равен 32 битам:

int max = 0x7fffffff;
double number = max;
number += 10.0;
double f = floor(number);
int oops = (int) f;

Я не могу сразу вспомнить, что делает C при переполнении преобразования с плавающей запятой в целое число...но это произойдет здесь.

Редактировать:Есть и другие интересные ситуации, которые следует рассмотреть.Вот некоторый код на C # и результаты - я бы, по крайней мере, предположил Похожие все должно было произойти в C.В C#, double определяется как 64 бита, и поэтому long.

using System;
class Test
{
    static void Main()
    {
        FloorSameInteger(long.MaxValue/2);
        FloorSameInteger(long.MaxValue-2);
    }

    static void FloorSameInteger(long original)
    {
        double convertedToDouble = original;
        double flooredToDouble = Math.Floor(convertedToDouble);
        long flooredToLong = (long) flooredToDouble;

        Console.WriteLine("Original value: {0}", original);
        Console.WriteLine("Converted to double: {0}", convertedToDouble);
        Console.WriteLine("Floored (as double): {0}", flooredToDouble);
        Console.WriteLine("Converted back to long: {0}", flooredToLong);
        Console.WriteLine();
    }
}

Результаты:

Исходное значение:4611686018427387903
Преобразовано в двойное:4.61168601842739E+18
Напольный (как двойной):4.61168601842739E+18
Преобразован обратно в длинный:4611686018427387904

Исходное значение:9223372036854775805
Преобразовано в двойное:9.22337203685478E+18
Напольный (как двойной):9.22337203685478E+18
Преобразован обратно в длинный:-9223372036854775808

Другими словами:

(long) floor((double) original)

это не всегда то же самое, что original.Это не должно вызывать удивления - существует больше длинных значений, чем double (учитывая значения NaN), и множество double не являются целыми числами, поэтому мы не можем ожидать, что каждое long будет точно представимо.Однако все 32-битные целые числа являются представимо в виде двойников.

Я думаю, вы немного озадачены тем, что хотите спросить. floor (3 + 0.5) не очень хороший пример, потому что 3, 0.5 и их сумма точно представлены в любом реальном формате с плавающей запятой. floor (0.1 + 0.9) был бы лучшим примером, и реальный вопрос здесь не в том, является ли результат floor точно представимым, а в том, неточность чисел перед вызовом floor будет возвращено значение, отличное от ожидаемого, если бы все числа были точными. В этом случае я считаю, что ответ - да, но это во многом зависит от ваших конкретных чисел.

Я приглашаю других критиковать этот подход, если он плохой, но одним из возможных решений может быть умножение вашего числа на (1.0 + 0x1p-52) или что-то похожее до вызова floor (возможно, было бы лучше использовать nextafter ). Это может компенсировать случаи, когда ошибка в последнем двоичном месте числа приводит к тому, что она падает чуть ниже целого значения, а не точно, но не учитывает ошибки, которые накопились за несколько операций. Если вам нужен такой уровень числовой стабильности / точности, вам нужно либо провести глубокий анализ, либо использовать библиотеку произвольной точности или математической точности, которая может корректно обрабатывать ваши числа.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top