Специальный тип-вычет для r-значений [дубликат]

Вы переполняете возможности типа номера JavaScript, см. §8.5 спецификации для деталей. Эти идентификаторы должны быть строками.

Плавающая точка с двойной точностью IEEE-754 (тип номера, используемого JavaScript) не может точно представлять все числа (конечно). Известно, что 0.1 + 0.2 == 0.3 неверно. Это может повлиять на целые числа, так же как и на дробные числа; он запускается после того, как вы получите более 9,007,199,254,740,991 (Number.MAX_SAFE_INTEGER).

Beyond Number.MAX_SAFE_INTEGER + 1 (9007199254740992), формат с плавающей запятой IEEE-754 больше не может представлять каждое последовательное целое число. 9007199254740991 + 1 - 9007199254740992, но 9007199254740992 + 1 также 9007199254740992, потому что 9007199254740993 не может быть представлен в формате. Следующим, что может быть, является 9007199254740994. Тогда 9007199254740995 не может быть, но 9007199254740996 может.

Причина в том, что у нас закончились биты, поэтому у нас больше нет 1-битного; бит младшего порядка теперь представляет кратность 2. В конечном счете, если мы продолжаем идти, мы теряем этот бит и работаем только в кратных 4. И так далее.

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


Если вам интересно узнать о битах, вот что происходит: двоичная двоичная двунаправленная плавающая переменная IEEE-754, номер точки имеет знаковый бит, 11 бит экспоненты (который определяет общий масштаб числа, как мощность 2 [потому что это двоичный формат]) и 52 бит значимости (но формат настолько умный, что он получает 53 бит точности из этих 52 бит). То, как используется показатель экспоненты, является сложным (, описанным здесь ), но в очень неопределенных терминах, если мы добавим его к экспоненте, значение знаменателя удваивается, так как показатель степени используется для степеней 2 (опять же, предостережение там, это не прямо, там есть умность).

Итак, давайте посмотрим на значение 9007199254740991 (aka, Number.MAX_SAFE_INTEGER):

   +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit
  / +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent
 / /        |  +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand
/ /         | /                                                  |
0 10000110011 1111111111111111111111111111111111111111111111111111
                = 9007199254740991 (Number.MAX_SAFE_INTEGER)

Это значение экспоненты 10000110011 означает, что каждый раз, когда мы добавляем его к значению, число, представленное, увеличивается на 1 (целое число 1, мы потеряли способность представлять дробные числа намного раньше).

Но теперь эта значимость полна. Чтобы пройти мимо этого числа, нам нужно увеличить показатель, что означает, что если мы добавим его к значению, значение представленного числа увеличится на 2, а не 1 (поскольку показатель применяется к 2, база этого двоичное число с плавающей запятой):

   +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit
  / +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent
 / /        |  +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand
/ /         | /                                                  |
0 10000110100 0000000000000000000000000000000000000000000000000000
                = 9007199254740992 (Number.MAX_SAFE_INTEGER + 1)

Ну, это нормально, потому что 9007199254740991 + 1 равно 9007199254740992. Но! Мы не можем представить 9007199254740993. У нас закончились бит. Если мы добавим только 1 к значению, он добавит 2 к значению:

   +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit
  / +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent
 / /        |  +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand
/ /         | /                                                  |
0 10000110100 0000000000000000000000000000000000000000000000000001
                = 9007199254740994 (Number.MAX_SAFE_INTEGER + 3)

Формат просто не может представлять нечетные числа, поскольку мы увеличиваем значение, показатель слишком велик.

В конце концов, мы снова закончим значащие биты и должны увеличить экспоненту, поэтому мы можем только представить кратность 4. Затем умножить на 8. Затем умножим на 16. И так далее.

4
задан user6000117 1 March 2016 в 01:40
поделиться

1 ответ

«Универсальная ссылка» (стандартным термином является ссылка на пересылку ) является (по определению) ссылкой rval на параметр cv-unqualified template, то есть T&&.

Vector<Size, TypeT>&& является ссылкой на rvalue, а не ссылкой на пересылку.

Если вы хотите получить значение аргументов шаблона, напишите признак:

template<class> struct vector_traits;

template<template<int, class TypeT> class Vector, int Size, typename TypeT>
struct vector_traits<Vector<Size, TypeT>>{
    static constexpr int size = Size;
    using value_type = TypeT;
};

И проверьте std::decay_t<T>:

template<class T>
void foo(T&& t) {
    using TypeT = typename vector_traits<std::decay_t<T>>::value_type;
    // use TypeT.
}

Вы также можете переместить его в аргумент шаблона по умолчанию, что делает foo SFINAE-friendly (он удаляется из набора перегрузки, если std::decay_t<T> не является «вектором» "):

template<class T,
         class TypeT = typename vector_traits<std::decay_t<T>>::value_type>
void foo(T&& t) {
    // use TypeT.
}
5
ответ дан T.C. 18 August 2018 в 06:39
поделиться