LINUX.ORG.RU

Авторы Си — наркоманы?

 , , ,


1

5

Столкнулся с интересным багом. После того как разобрался, что же именно происходит, меня постигло крайнее изумление! Оказывается, в языке Си тип числовой константы зависит от формата записи.

Дистиллированный пример кода, который это демонстрирует:

#include <stdbool.h>
#include <stdio.h>

#define IS_HEX(x) \
    _Generic((x), \
        unsigned int: true, \
        long: false \
    )

#define X 0x80000001
#define I 2147483649

int main(void) {
    if(X == I)
        puts("X == I");

    if(!IS_HEX(I))
        puts("I is not hexadecimal");

    if(IS_HEX(X))
        puts("X is hexadecimal");

    return 0;
}

Все три сообщения будут выведены на экран.

Зачем это сделано? Кому от этого легче? Какие оптимизации это позволяет проворачивать, кроме оптимизации отстрела ног программистам? Непонятно! В общем, стремлюсь поделиться своим негодованием здесь и предостеречь будущие поколения от наступления на эти грабли.



Последнее исправление: dataman (всего исправлений: 1)
Ответ на: комментарий от monk

time_t вроде как изначально был int32_t, сейчас int64_t, как по мне в прикладном софте его лучше не трогать пока все на 64 не перейдут (а до 32 года не так далеко)

Ну либо как извращенный вариант хранить в int64_t и кастовать в time_t в надежде что компилятор современный(подобие шутки))

Долговато я что-то тупил чтоб это вспомнить))

Slackware_user ★★★★★
()
Последнее исправление: Slackware_user (всего исправлений: 1)
Ответ на: комментарий от Slackware_user

Ничего не понял, что там «надо смотреть». Компиляторы для доса 16-битные, int там тоже 16-битный (или у тебя по каким-то причинам этот общеизвестный факт вызывает сомнения?). Расширители это другое, я про них не думал, но на факт 16-битности обычного дос-софта они никак не влияют.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Я не уверен что во всех моделях памяти у ваткома int==short

согласно ману у них 2 типа интов - long и short, для 16 бит short == int

Подробнее не вчитывался если честно.

Slackware_user ★★★★★
()
Ответ на: комментарий от Slackware_user

Во-первых, почему такое акцентирование на ваткоме? Дефолтный си-компилятор для доса скорее турбо си. Во-вторых, на 16-битных платформах делать int не 16-битным было бы крайней степенью идиотизма, вне зависимости от оправданий. Потому как такой компилятор бы тайпкастил всю арифметику (см. integer promotion) к неудобной для проца разрядности и делал бы таким образом на ровном месте тормозящие в несколько раз проги, не говоря уж про такие мелочи как двукратный жор памяти на все int-ы тоже на ровном месте. А ещё int раньше позиционировался как наиболее нативный для проца тип, с этим тоже расхождение получится. Сейчас это уже нарушили (на 64бит платформах), но тут нарушили ради того чтобы не сломалась куча старого кода. А зачем это было бы делать там не представляю.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от Slackware_user

time_t вроде как изначально был int32_t, сейчас int64_t

Так time_t вроде для того и изобрели, чтобы унифицировать uint32 / uint64

COKPOWEHEU
()
Ответ на: комментарий от COKPOWEHEU

С time_t вообще странно, в старых функциях имеется тенденция передавать его указателем (time(&t), localtime(&t)), как будто что-то может не позволить его передать значением. А ещё я в одном старом коде (до-amd64) видел вычисление разницы двух time_t с кастом или кастами к double.

Не удивлюсь если где-то в древности time_t был вообще не целочисленным типом, а дробным или структурой, или хотя бы были такие планы.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от firkax

видел вычисление разницы двух time_t с кастом или кастами к double.

Что за извращение.

Не удивлюсь если где-то в древности time_t был вообще не целочисленным типом

Вот это вряд ли. Уж от Си-шников такое никак не ожидаешь.

COKPOWEHEU
()
Ответ на: комментарий от firkax

Не удивлюсь если где-то в древности time_t был вообще не целочисленным типом, а дробным или структурой, или хотя бы были такие планы.

С плавающей точкой в стандарте точно был. Ещё в 2001 году

Only
     the following properties are guaranteed by the IEEE Std 1003.1-2001
     ("POSIX.1") standard:
...
           4.   The time_t and clock_t types are either integers or real-
                floating types.  

(https://www.daemon-systems.org/man/types.3.html)
monk ★★★★★
()
Ответ на: комментарий от monk

А ещё есть замечательное:

double difftime(time_t tim1, time_t tim2);

Разница в секундах между двумя time_t должна быть типа double.

monk ★★★★★
()
Ответ на: комментарий от monk

А, так это стандартная функция. Вот где я double и видел.

А в мане к ней вот что:

NOTES
       On a POSIX system, time_t is an arithmetic type, and one could just de‐
       fine

           #define difftime(t1,t0) (double)(t1 - t0)

       when the possible overflow in the subtraction is not a concern.
То есть, в каких-то non-POSIX системах, time_t могло быть не числовым.

Хотя смысл делать double для ответа есть даже если time_t - signed int. Потому что вычитая два int-а можно получить переполнение, а вот после кастования их (ещё до вычитания) к double переполнения уже быть не сможет.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)

Какую тему я пропустил! ТС упоролся, это как критиковать что вода слишком мокрая

I-Love-Microsoft ★★★★★
()
Ответ на: комментарий от firkax

Все известное из игрушек под дос компилилось Watcom.

Большая часть софта прикладного был masm.

Turbo Pascal живьем видел, а вот Turbo C не.

Был еще microsoft c, там типы как для win16 само собой

Slackware_user ★★★★★
()
Ответ на: комментарий от firkax

signed int в 2032 станет тыквой, хотя смысл каста в дабл результата все одно не вижу.

зачем время в floating point?

еще б деньги float считать…

Slackware_user ★★★★★
()
Ответ на: комментарий от Slackware_user

типы как для win16 само

Да что ж такое опять!? win16 это поздняя оболочка для доса! Это в «win16 типы как в досе16», а не наоборот.

firkax ★★★★★
()
Ответ на: комментарий от Slackware_user

signed int в 2032 станет тыквой, хотя смысл каста в дабл результата все одно не вижу.

Каст результата в double - только для совместимости. А вот оригинальное difftime могло и что-то другое делать. Каст операндов в double перед вычитанием чтобы не получить чушь из INT_MAX-INT_MIN. То, что отрицательные timestamp обычно не используются - не повод делать чтобы твоя прога от них глючила.

зачем время в floating point?

Вероятно затем что бы доли секунд учитывать без timeval/timespec и прочего мазохизма. Тут в целом три варианта, и у всех есть недостатки:

1) timeval/timespec, где доли секунды в отдельном целом типе, недостаток - это не арифметический тип получается, его нельзя без возни складывать/вычитать итд (возможно кстати какие-то древние time_t такими и были, от того и процитированное пояснение из мана)

2) дробный тип, недостаток - молчаливые округления из дробной арифметики, невозможность гарантии точности вычислений

3) счётчик милли/микро/наносеунд одним целым числом (уже 64-битное если надо реальное время так хранить), недостаток - невозможность просто прочитать из него секунды без делений, а так же мучительные размышления на счёт того, как обрабатывать врап, который исходно то врап и не ломает арифметику, а вот после деления на например 1000000000 будет уже багопровоцирующим закруглением после приблизительно 18.4 миллиардов секунд.

firkax ★★★★★
()

Мне казалось это всем известно, они не наркоманы, они алкоголики. Только для С/С++ вычислена точка Балмера.

kirill_rrr ★★★★★
()
Ответ на: комментарий от firkax

microsoft c позже win 3.1 если правильно помню. До этого был quick c а по факту масм и гвбейсик.

Slackware_user ★★★★★
()
Ответ на: комментарий от firkax

Ну double то точность теряет поскольку дискретный достаточно сильно.

Unix time в секундах, есть вроде бы апи дергающее кратные части секунды, но там все равно целые

Slackware_user ★★★★★
()
Ответ на: комментарий от Slackware_user

Ну double то точность теряет поскольку дискретный достаточно сильно.

Чего?

Unix time в секундах, есть вроде бы апи дергающее кратные части секунды, но там все равно целые

Вот смотри, я пишу:

1) timeval/timespec

ты отвечаешь:

есть вроде бы апи ... кратные части секунды

вроде

Это ты дотуда не дочитал или ты не в курсе как называется структура для времени с точностью до микро/наносекунд?

firkax ★★★★★
()
Ответ на: комментарий от firkax

Последние лет 7 фортран\паскаль\питон. Потихньку начинаю забывать.

Столярова к своему стыду далее середины 1 тома пока некогда.

Потому и ВРОДЕ.

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

float использовать конечно можно но только там где числа вообще ни на что не влияют.

Slackware_user ★★★★★
()
Ответ на: комментарий от firkax

К тому что float - имитация вещественных чисел. И при определенных условиях дает дичь. И сделать с этим что либо сложно.

Slackware_user ★★★★★
()
Ответ на: комментарий от Slackware_user

float и double это разные типы, у первого 24 значащих бита, у второго 53.

Никакой из них не имитация. Про «весьма дискретность» до сих пор непонятно.

firkax ★★★★★
()
Ответ на: комментарий от Slackware_user

Ну double то точность теряет

Он теряет точность если надо выравнивать порядки. А пока не кончатся биты мантиссы, в этом нет нужды. Длина мантиссы у double 53 бита. То есть int53_t. Если пока под секунды хватает 32 бит, на дробную часть останется 21 бит. Дискретность примерно пол-микросекунды.

Это не отменяет неоправданные сложности использования дробных чисел в качестве простого счетчика.

COKPOWEHEU
()
Ответ на: комментарий от Slackware_user

Для счетчиков все равно плохо. Часть битов теряется впустую, сами вычисления существенно более сложные (особенно если нет FPU). Да еще и точность может внезапно измениться.

COKPOWEHEU
()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.