Есть ли какой-либо встроенный способ получить мантиссу и показатель степени (значения «научной нотации» или «электронной нотации») CGFloat в Swift?

Я был ошеломлен, узнав, что нет? немедленный способ получить мантиссу/показатель степени CGFloat в Swift.

(Обратите внимание, что мне нужны два значения, а НЕ строковое представление в формате научной записи.)

Эти безнадежно недокументированные свойства Double.expose , Double.significand находятся в Double, но это сильно отличается от желания иметь «обычную, человеческую» мантиссу/показатель степени. (Если я чего-то сильно не упускаю.)

Я напечатал функцию для этого, но это чушь, по крайней мере, по трем основным причинам, которые приходят на ум.

Есть ли решение этой загадки?

(*) Удивлен, что здесь нет 100 вопросов по этой проблеме!

extension CGFloat { // (ignored negatives for clarity)
    var decomp: (CGFloat, CGFloat) {
        var exponent: CGFloat = 0.0
        var mantissa: CGFloat = self
        while mantissa < 1.0 {
            mantissa = mantissa * 10.0
            exponent -= 1
        }
        while mantissa >= 10.0 {
            mantissa = mantissa / 10.0
            exponent += 1
        }
        print("check ... ", self, "\(mantissa)E\(exponent)")
        return (mantissa, exponent)
    }
}

следовательно ..

var x: CGFloat = 0.00123
print( "yo" , x.decomp)
x = 12
print( "yo" , x.decomp)
x = 1000
print( "yo" , x.decomp)
check ...  0.00123 1.23E-3.0
yo (1.23, -3.0)
check ...  12.0 1.2E1.0
yo (1.2, 1.0)
check ...  1000.0 1.0E3.0
yo (1.0, 3.0)

(†) Minor - I return the exp as a float since that seemed more consistent for the typical ways you'd then use such a thing, but IDK.

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
56
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Свойства exponent и significandDouble дают вам двоичную мантиссу и показатели степени. В конце концов, Double — это двоичный тип с плавающей запятой.

Похоже, вы хотите получить показатель степени и мантиссу по основанию 10. Для этого вам следует использовать Decimal, который представляет число по основанию 10.

extension CGFloat {
    // this does not consider "special" values like infinities and NaNs
    var base10ExponentAndSignificand: (CGFloat, CGFloat) {
        let decimal = Decimal(self)
        
        return (
            CGFloat(truncating: decimal.significand as NSNumber) * (decimal.isSignMinus ? -1 : 1),
            CGFloat(decimal.exponent)
        )
    }
}

Это даст целые числа n, m, где исходное число равно n * pow(10, m).

Научное обозначение обычно записывается как nEm, где n — число от 1 до 10. Если вы хотите, чтобы n было от 1 до 10, я бы просто использовал log10 для вычисления показателя степени вместо подхода цикла, показанного в вашем вопросе:

extension CGFloat {
    // this does not consider "special" values like infinities and NaNs
    var base10ExponentAndSignificand: (CGFloat, CGFloat) {
        guard self != 0 else { return (0, 0) }
        
        let exp = log10(self.magnitude).rounded(.down)
        let significand = self / pow(10, exp)
        
        return (significand, exp)
    }
}

Боже мой! По сути, секрет в том, чтобы использовать Decimal, а не Double!! Я просто не подумал об этом! Думаю, в названии «Decimal» было что-то хитрое, что меня сбило с толку :)

Fattie 04.06.2024 12:55

@Fattie Вот как они реализовали Decimal.init(Double) в Swift-corelibs-foundation. Он использует циклы, аналогичные тем, которые вы используете. Не уверен, что это поможет.

Sweeper 04.06.2024 14:02

Вот посмотрите на результаты трех способов сделать это:

Имейте в виду, что мантисса часто «неправильная», отклоняясь на несколько цифр. Это относится ко всем трем методам. :/

0,23

using 'division' method
 (mantissa: 2.3000000000000003, exponent: -1.0)
using log method
 (normalizedSignificand: 2.3, normalizedExponent: -1.0)
via Decimal method (gives 'random' pairs, not scientific notation pairs)
 (notNormalizedSignificand: 2.3000000000000005e+18, notNormalizedExponent: -19.0)
using sprintf %E
 2.300000E-01

 0.000357
using 'division' method
 (mantissa: 3.5700000000000003, exponent: -4.0)
using log method
 (normalizedSignificand: 3.57, normalizedExponent: -4.0)
via Decimal method (gives 'random' pairs, not scientific notation pairs)
 (notNormalizedSignificand: 357.0, notNormalizedExponent: -6.0)
using sprintf %E
 3.570000E-04

 0.000857
using 'division' method
 (mantissa: 8.57, exponent: -4.0)
using log method
 (normalizedSignificand: 8.57, normalizedExponent: -4.0)
via Decimal method (gives 'random' pairs, not scientific notation pairs)
 (notNormalizedSignificand: 857.0, notNormalizedExponent: -6.0)
using sprintf %E
 8.570000E-04

 0.000127
using 'division' method
 (mantissa: 1.27, exponent: -4.0)
using log method
 (normalizedSignificand: 1.27, normalizedExponent: -4.0)
via Decimal method (gives 'random' pairs, not scientific notation pairs)
 (notNormalizedSignificand: 127.0, notNormalizedExponent: -6.0)
using sprintf %E
 1.270000E-04

 66.0
using 'division' method
 (mantissa: 6.6, exponent: 1.0)
using log method
 (normalizedSignificand: 6.6, normalizedExponent: 1.0)
via Decimal method (gives 'random' pairs, not scientific notation pairs)
 (notNormalizedSignificand: 66.0, notNormalizedExponent: 0.0)
using sprintf %E
 6.600000E+01

 0.2
using 'division' method
 (mantissa: 2.0, exponent: -1.0)
using log method
 (normalizedSignificand: 2.0, normalizedExponent: -1.0)
via Decimal method (gives 'random' pairs, not scientific notation pairs)
 (notNormalizedSignificand: 2.0, notNormalizedExponent: -1.0)
using sprintf %E
 2.000000E-01

 0.6
using 'division' method
 (mantissa: 6.0, exponent: -1.0)
using log method
 (normalizedSignificand: 5.999999999999999, normalizedExponent: -1.0)
via Decimal method (gives 'random' pairs, not scientific notation pairs)
 (notNormalizedSignificand: 6.0, notNormalizedExponent: -1.0)
using sprintf %E
 6.000000E-01

 0.8
using 'division' method
 (mantissa: 8.0, exponent: -1.0)
using log method
 (normalizedSignificand: 8.0, normalizedExponent: -1.0)
via Decimal method (gives 'random' pairs, not scientific notation pairs)
 (notNormalizedSignificand: 8.0, notNormalizedExponent: -1.0)
using sprintf %E
 8.000000E-01

Похоже, что использование %E с форматировщиком строк волшебным образом «работает» во всех случаях. Остальные три не «работают».

То есть вам просто не нравятся такие незначительные ошибки, как 0,0000000000000003? Это просто потому, что математика с плавающей запятой не работает . Подход Decimal имеет ту же проблему (но, возможно, реже) для некоторых конкретных значений (попробуйте 0,23). Вы никогда не сможете точно преобразовать двоичный код в десятичный для всех чисел. Это также причина, по которой инициализатор Decimal.init(String) предпочтительнее того, который принимает Double.

Sweeper 04.06.2024 14:30

«Значит, вам просто не нравятся мелкие ошибки типа: «Конечно, я имею в виду именно это». Есть множество случаев, когда этого следует избегать. И да, я с грустью осознаю, что математика с плавающей запятой - это проклятие жизни :) Ах, спасибо за указание на то, что десятичная дробь действительно та же проблема, я не знал - отредактировал это демо.

Fattie 04.06.2024 15:52

Я только что провел быстрый тест, и действительно, %E, похоже, всегда «работает». Интересно, они очень просто округляют до 6 мест или есть еще какой-нибудь трюк!

Fattie 04.06.2024 16:03

Критерием, вероятно, является «столько цифр, сколько необходимо, чтобы отличить это число от его соседей (плюс/минус 1 ulp)». См., например, Java Double.toString. docs.oracle.com/javase%2F8%2Fdocs%2Fapi%2F%2F/java/lang/…

Sweeper 04.06.2024 16:11

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

Похожие вопросы

Размещение представления в виде снизу в другом представлении не работает должным образом при использовании SwiftUI
Нет такого модуля «UIKit» для проекта SwiftUI
Можно ли добавить собственный переход к дочерним элементам TabView при изменении страницы?
Основное статическое свойство «текст», изолированное от актера, не может быть изменено из неизолированного контекста; это ошибка в Swift 6
Полная ширина UICollectionViewCells перекрывается во время вращения ориентации интерфейса, вызывая некрасивую анимацию
Каков правильный подход к модульному тестированию в iOS?
Разве UILabel sizeThatFits не должен возвращать размер, меньший или равный заданному размеру, а не больше, для однострочных меток?
Как центрировать UIImageView вертикально в UICollectionViewListCell в качестве специального аксессуара?
Предупреждения о риске зависания Xcode
При прокрутке высота ячейки рассчитывается не правильно?