LINUX.ORG.RU

Говорят *int64_t не нужен. float64_t хватит всем

 , , , ,


1

4

Непопулярное мнение: JavaScript единственный язык, который сделал правильные числа. 64 бита, которые одновременно и целое до 53-х бит, и с плавающей точкой если больше.

Ну просто подумайте: в какой жизненной ситуации вам нужны целые больше 53-х бит? Это 9*10^15, девять ебучих квадриллионов, девять тыщ триллионов то есть. Чтобы представлять что? Вокруг нас просто нет предметов в таком количестве (кроме размера node_modules). Девять тыщ терабайт, если в байтах.

То есть 53 бита достаточно каждому. Даже в указателях только 48 используется (хаха, а вы что, думали 64? Щас).

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

Почему же тогда подход JS не нравится «настоящим программистам»? Да потому что все остальные наловчились в 64 бита рассовывать разное, нужно им это или не нужно.

То есть по сути единственная причина, почему это неудобно, это другие языки, которые пришли раньше и у которых конвенция другая. Они может все 64 бита и не используют, но _потенциально_ могут, и в итоге приходится под них подстраиваться.

А нужно наоборот, чтобы все подстраивались под JavaScript (который на самом деле IEEE 754 double-precision float, если вам так больше нравится).

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

А всякие int64/uint64 это от лукавого. Должен быть только number64.

★★★★

intptr_t например (что если адресное пространство больше 53 бит?)
Циклические счётчики, когда тебе важна разность между моментами 2 событий, а не абсолютное значение. тут конечно и 32 бита обычно хватает, но гибридный тип с float'ом не пойдёт.
Да и вообще иногда нужны именно целочисленные операции, плавающая точка может внезапно потерять где-нибудь точнось, особенно при оптимизации. с целыми работать проще.

mittorn ★★★★★
()

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

apt_install_lrzsz ★★★
()

Пусть он обоснует что (a + b) + c != a + (b + c) – это правильный подход и так и должно быть. А то, понимаешь ли, привыкли тут что сложение ассоциативно!

Python 3.11.6 (main, Oct  2 2023, 13:45:54) [GCC 12.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = float(2**64)
>>> b = -a
>>> a
1.8446744073709552e+19
>>> b
-1.8446744073709552e+19
>>> c = 1
>>> (a + b) + c
1.0
>>> a + (b + c)
0.0
hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 3)

Странно иметь настолько мало опыта в программировании чтобы не знать миллиона применений для 64-битных интов, и при этом иметь наглость диктовать какие типы для чего применимы и кто под кого должен подстаиваться.

anonymous
()

Почему же тогда подход JS не нравится «настоящим программистам»? Да потому что все остальные наловчились в 64 бита рассовывать разное

ровно наоборот. Это в JS целые 53 бита, потому что они наловчились рассовывать разное в 64 бита (в double).

Это сайд эффект от NaN-packed/NaN-boxing

MKuznetsov ★★★★★
()

Who are you quoting?

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

В JavaScript два числовых типа:

> 1 === 1n
< false

shdown
()

На бинарном уровне double (aka float64) все равно является числом с плавающей точкой, только из-за того что оно имеет достаточно разрядов оно не теряет точность при обратном преобразовании из double -> long.

На wiki:

0 01111111111 00000..0   = 1
0 10000000000 00000..0   = 2
0 10000000000 10000..0   = 3   # math.pow(2,1)*(1 + 0.5) 
0 10000000001 00000..0   = 4   # math.pow(2,2)*(1 + 0.0)
0 10000000011 01110..0   = 23  # math.pow(2,4)*(1 + 0.25 + 0.125 + 0.0625)

Если кто не поняли или не знает как кодируются числа с плавающей запятой то поясню:

2^{экспонента} * 1.{мантисса}

А если экспонента 0 (магическое число) то просто:

0.{мантисса} 

где каждый бит в мантиссе обозначает доли 0.5 0.25 0.125 0.0625 и т.д. их сумма будет приближаться к 1 (чем больше разрядов в мантиссе тем ближе к единице).

Короче, FP это жесть, преобразование int-> float не очевидно, на бинарном уровне с ними работать это стрелять себе в ногу. Обязательно найдутся double значения отличающиеся на бинарном уровне но преобразующиеся в единственное целое.

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

Добавлю, я думаю можно попасть на вычислении хеша, например потерять правильную корзину в какой-нибудь хеш-карте если что-то сделать немного не так, например если может поменяться порядок операций (допустим хеш вычисляется на одной машине а потом передается на другую), не говоря уже про компиляцию с флагом ffast-math.

anonymous
()

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

На каких бл. байтах? Тип данных какой использовать по мнению автора сего убожества?

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

А если экспонента 0 (магическое число) то просто: 0.{мантисса}

Немножко не так. Если экспонента 0, то это либо 0, когда мантисса = 0, либо денормализованные числа порядка 2^-1022 и меньше, то есть очень маленькие - чтобы увеличить точность операций вокруг нуля.

Денормализованные числе часто округляют до 0, при оптимизациях. Кроме того, не все архитектуры в принципе их поддерживают.

https://en.wikipedia.org/wiki/Subnormal_number

преобразование int-> float не очевидно

Абсолютно. Не найдётся ни одного такого флоата, кроме нуля, которое бы имело то же самое битовое представление, что и равное ему целое той же разрядности.

anonymous
()

в какой жизненной ситуации вам нужны целые больше 53-х бит? Это 9*10^15, девять ебучих квадриллионов, девять тыщ триллионов то есть. Чтобы представлять что? Вокруг нас просто нет предметов в таком количестве

Эм, а по эллиптическим кривым как гулять? Например, в secp256k1 (используется в bitcoin и некоторых алгоритмах шифрования) нужно 10^78. Ваши 9*10^15 вообще ни туда ни сюда.

Obezyan
()

одновременно и целое до 53-х бит, и с плавающей точкой если больше.

Неявное преобразование типов с потерей данных, да ещё и в рантайме? Великолепно!

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

Давайте всё таки будем честны: 64 бита действительно часто используется, как 8 байт в памяти, в которые удобно распиханы данные, не имеющие отношения к тому числу, которое там должно было быть.

Snowflake id, tagged pointer, что-то ещё. Это всё примеры структур, спрятанных под 64 бита и действительно для большинства практических целей хватит и 53 бит, и 48 бит.

Мы когда делали себе в софтине самопальную timeseries db, то сократили числа до 12 бит, а в оставшиеся 2 бита клали разряд. Очень удобно, потому что на графике точность выше 12 бит не нужна никак.

Но бесит то нас не то, что кто-то указывает на то, что мы «неправильно» пользуемся 64 битами, а то, что приводит в пример яваскрипт с его больными умолчаниями.

018 == "018"
017 != "017"

Люди, которые породили такое, не могут быть примером для остальных.

В том же руби есть автобоксинг интов: дошли до 62 бит и потом число само превращается в BigNumber объект с соответствующим пенальти. А вместо этого в яваскрипте нет возможности распарсить длинный интеджер, который встречается на практике

Т.е. яваскрипт и в теории плохой и на практике плохой. И бесит то, что это предлагают как пример только потому, что видите ли мы абъюзим эти 8 байт.

Да абъюзим, потому что мы абъюзеры и будем такими. И петушня позорная нам не указ.

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

Даже в указателях только 48 используется (хаха, а вы что, думали 64? Щас).

Откуда дровишки? Разницу между физическими и виртуальными адресами мы точно понимаем?

Мы – да. Ты – не факт.

~ cat /proc/cpuinfo|grep address\ sizes|uniq
address sizes   : 43 bits physical, 48 bits virtual

Виртуальные адреса, конечно, 64 бита на бумаге, но в середине там нули, поэтому они укладываются в 48 бит. Это для x86_64. Другие архитектуры умеют в том числе полноценное 64-битное виртуальное пространство.

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

64 бита на бумаге, но в середине там нули

там не нули :-) там значение равное старшему значащему биту адреса (тому самому 47-му).

Да, ты прав. Мой косяк.

The x86-64 architecture divides canonical addresses into two groups, low and high. Low canonical addresses range from 0x0000'0000'0000'0000 to 0x0000'7FFF’FFFF’FFFF. High canonical addresses range from 0xFFFF'8000'0000'0000 to 0xFFFF’FFFF’FFFF’FFFF. Considered as signed 64-bit numbers, all canonical addresses range between -2^47 and 2^47-1.
hateyoufeel ★★★★★
()

1) Производительность. Некоторые операции физически так устроены, что работают только с целыми. Помимо битовых операций очевидное индексирование массивов. Конверсия будет добавлять время. Плюс я не уверен, что даже на современных процессорах вещественные операции не медленнее целых. Особенно если помимо десктопов думать о мобильных процессорах. И я уж молчу про микроконтроллеры, где часто вообще soft float.

2) Защита от ошибок. Где-то вещественное не имеет смысла. И сами типы тебя защищают от них. А с float у тебя получится полтора землекопа, а ты и не заметишь. Math.floor постоянно вызывать геморно.

3) 64 бита таки бывают нужны. Например, таймстемпы высокой точности, хеши, криптография. И битовые операции там тоже нужны, не как хак для производительности, а как официальная часть алгоритма. Если ты не дорос до таких задач, это не значит, что их нет.

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

Я бы понял если допустим над единым форматом можно было бы и битовые операции и флоат и целые проводить. Естественно после битовых в флоатах хз что.

Ещё 53 бита это как то не кругло. 48 + 16 в угоду скорости и меньше точности. + чёткая адресация сейчас. Имхо

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

Виртуальные адреса, конечно, 64 бита на бумаге

Таки да - для меня было немножко сюрпризом что виртуальные адреса на x86-64 тоже ограничены. Вообще - логично конечно.

А теперь, внимание - вопрос на миллион: так что, получается старшие 16 бит в поинтерах на x86-64 можно абъюзить (по крайней мере на нынешнем железе)?

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

Не знаю. В TLB - навряд ли. Если хочешь углубиться в вопрос и выяснить, возможно ли заюзать «неиспользуемые битики» на уровне ядра и page table, то вот тебе пара ссылок для начала:

https://docs.kernel.org/mm/page_tables.html

https://www.kernel.org/doc/gorman/html/understand/understand006.html

На уровне юзерспейса это точно не работает.

anonymous
()

JavaScript единственный язык, который сделал правильные числа.

JavaScript единственный язык, который сделал правильные

JavaScript единственный язык

А чего мелочится?! Даешь JS единственным ЯП с обязательным нативным исполнением на любом железе!

То есть JS достаточно каждому. Даже в embedded используется (хаха, а вы что, думали только Сишечка и Асм? Щас). Почему же тогда JS не нравится «настоящим программистам»? Да потому что все остальные наловчились в синтаксис рассовывать разное, нужно им это или не нужно. То есть по сути единственная причина, почему JS не единственный, это другие языки, которые пришли раньше и у которых конвенция другая. Они может все возможности синтаксиса и не используют, но потенциально могут, и в итоге приходится под них подстраиваться. А нужно наоборот, чтобы все подстраивались под JavaScript. Но как же адресная арифметика, скажете вы? Как мне всякие там офсеты считать? Ну чувак, адресная арифметика на указателях это страшный хак, раз уж мы о типах заговорили. Она должна на массивах делаться, а не на указателях. А всякие Сишечки, Java, Rust, Asm, PHP, Python это от лукавого. Должен быть только JavaScript.

QsUPt7S
()

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

Shadow ★★★★★
()