2016-08-05 2 views
-4

Привет У меня есть два вопроса:C - Unsigned долго долго удваивать на 32-битной машине

  1. uint64_t против двойника, который имеет более высокий предел диапазона для покрытия положительных чисел?

  2. Как конвертировать double в uint64_t, если требуется только целое число в двойном размере.

Непосредственное кастинг, по-видимому, не работает из-за того, как определено двойное.

Извините за любую путаницу, я говорю о 64-битном двойном в C на 32-битной машине.

Как для примера:

//operation for convertion I used: 
double sampleRate = (
         (union { double i; uint64_t sampleRate; }) 
         { .i = r23u.outputSampleRate} 
        ).sampleRate; 

//the following are printouts on command line 
//   double        uint64_t 
//printed by %.16llx       %.16llx 
outputSampleRate 0x41886a0000000000  0x41886a0000000000 sampleRate 

//printed by %f         %llu 
outputSampleRate 51200000.000000  4722140757530509312 sampleRate 

Таким образом, две цифры остаются теми же битовый шаблон, но при печати в качестве знаков после запятой, то uint64_t совершенно неправильно. Спасибо.

+0

Вы говорите о 'double', как указано в C, или' double', как в ieee754 binary64 с плавающей запятой? – EOF

+1

За # 2 вы пытались просто назначить двойную переменную? –

+1

Что вы подразумеваете под «прямым кастингом, по-видимому, не работает»? Как это не работает? Приведите пример номера и того, что вы получили, когда вы его удвоили, и как вы изучили его значение. – FredK

ответ

5

uint64_t vs double, который имеет более высокий предел диапазона для покрытия положительных чисел?

uint64_t, где поддерживается, имеет 64 бита значения, без битов дополнений и без знака. Он может представлять все целые числа от 0 до 2 - 1 включительно.

Практически все современные реализации C представляют double в 64-битном двоичном формате IEEE-754, но C не требует и даже не поддерживает этот формат. Однако настолько распространено, что достаточно безопасно предположить этот формат и, возможно, просто провести некоторые проверки времени компиляции против макросов, определяющих характеристики FP. Я полагаю, что для баланса этого ответа реализация C действительно использует это представление.

Бинарная двойная точность IEEE-754 обеспечивает 53 бит мантиссы, поэтому она может представлять все целые числа от 0 до 2 - 1.Это формат с плавающей запятой, однако с 11-битным двоичным показателем. по величине число он может представлять собой (2 - 1) * 2 , или почти 2 . В этом смысле double имеет гораздо больший диапазон, чем uint64_t, но подавляющее большинство целых чисел от 0 до его максимального значения не может быть представлено точно как double с, включая почти все числа, которые может быть представлен точно на uint64_t.

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

Вы можете просто назначить (преобразование неявно), или вы можете явно бросить, если вы хотите, чтобы понять, что происходит преобразование:

double my_double = 1.2345678e48; 
uint64_t my_uint; 
uint64_t my_other_uint; 

my_uint = my_double; 
my_other_uint = (uint64_t) my_double; 

Любой дробная часть от стоимости double «s будет обрезана. Целочисленная часть будет сохранена точно, если она представима как uint64_t; в противном случае поведение не определено.

В представленном вами коде используется объединение для наложения памяти double и uint64_t. Это не является по своей сути неправильным, но это не полезный метод для преобразования двух типов. Cast - это механизм C для всех неявных преобразований значений.

+0

C11 draft standard n1570 * 6.3.1.4 Реальные плавающие и целочисленные Когда конечное значение реального плавающего типа преобразуется в целочисленный тип, отличный от _Bool, дробная часть отбрасывается (т. Е. Значение усекается к нулю). Если значение целой части не может быть представлено целым типом, поведение не определено. * – EOF

+0

@EOF, спасибо за эту коррекцию. Я обновил свой ответ. –

1

double может содержать существенно большее число, чем uint64_t, как диапазон значений для 8 байт IEEE 754 является 4.94065645841246544e-324d к 1.79769313486231570e + 308D (положительный или отрицательный) [taken from here][more detailed explanation]. Однако, если вы добавите небольшие значения в этом диапазоне, вы будете удивлены, потому что в какой-то момент точность не сможет представить, например. добавление 1 и округление до нижнего значения, по существу, создание цикла, неуклонно возрастающего на 1 без конца.

Этот код, например:

#include <stdio.h> 
2 int main() 
3 { 
4  for (double i = 100000000000000000000000000000000.0; i < 1000000000000000000000000000000000000000000000000.0; ++i) 
5   printf("%lf\n", i); 
6  return 0; 
7 } 

дает мне постоянный выход 100000000000000005366162204393472.000000. Вот почему у нас есть функции next-after и nexttoward в math.h. Вы также можете найти там функции ceil и floor, которые теоретически позволят вам решить вашу вторую проблему: удаление части дроби.

Однако, если вам действительно нужно держать большие числа, вы должны посмотреть на реализации bigint, например, GMP. Bigints были разработаны для выполнения операций с очень большими целыми числами, а операции, такие как добавление одного, действительно увеличивают число даже для очень больших значений.

Смежные вопросы