Quando um trigonometria deve funcionar, com um argumento de grau, retornar -0,0?

10

Na criação de funções trigonométricas my_sind(d), my_cosd(d), my_tand(d), que usou um argumento grau em vez de um radiano e forneceu respostas exatas em múltiplos de 90, eu notei que o resultado foi, por vezes, -0.0em vez de 0.0.

my_sind( 0.0) -->  0.0
my_sind(-0.0) --> -0.0

my_sind(180.0) --> -0.0
my_sind(360.0) -->  0.0

sin()e tan()geralmente retornam o mesmo resultado de zero de sinal para uma determinada entrada de zero de sinal. Faz sentido que my_sin()corresponda sin()a essas entradas.

my_sind( 0.0) alike sin( 0.0) -->  0.0
my_sind(-0.0) alike sin(-0.0) --> -0.0

A questão é : por que número inteiro non_zero_ndeve / pode o resultado nunca voltar -0.0para my_sind(180*non_zero_n), my_cosd(180*n + 180), my_tand(180*non_zero_n)?

É fácil codificar, portanto, apenas f(-0.0)produz -0.0e termina com ele. Simplesmente me pergunto se existe algum motivo para fazer outro f(x) retorno -0.0para outro ( diferente de zero ) xe a importância de garantir esse sinal.


Nota: Esta não é uma questão de por que 0.0vs. -0.0ocorre. Não é por isso cos(machine_pi/4)que não retorna 0.0. Nem isso é uma questão de como controlar a geração de 0.0ou -0.0. Eu vejo isso melhor como uma questão de design.

chux - Restabelecer Monica
fonte

Respostas:

4

O princípio de design da "menor surpresa" sugere que procuremos orientação para funcionalidades previamente estabelecidas. Nesse caso, a funcionalidade mais próxima estabelecida é fornecida por sinpie cospiintroduzidas no IEEE Std 754-2008 (padrão IEEE para aritmética de ponto flutuante), seção 9. Essas funções não fazem parte dos padrões atuais ISO C e ISO C ++, mas foram incorporados às bibliotecas matemáticas de várias plataformas de programação, por exemplo, CUDA.

Essas funções calculam sin (πx) e cos (πx), onde a multiplicação com π ocorre implicitamente dentro da função. tanpinão está definido, mas pode-se, com base na equivalência matemática, fornecer funcionalidade de acordo com tanpi(x) = sinpi(x) / cospi(x).

Podemos agora definir sind(x) = sinpi(x/180), cosd(x) = cospi(x/180), tand(x) = tanpi(x/180)de uma forma intuitiva. A Seção 9.1.2 da IEEE-754 especifica o tratamento de argumentos especiais para sinpie cospi. Em particular:

sinPi (+ n) é +0 e sinPi (−n) é −0 para números inteiros positivos n. Isso implica, sob modos de arredondamento apropriados, que sinPi (−x) e −sinPi (x) são o mesmo número (ou ambos NaN) para todos os x. cosPi (n + ½) é +0 para qualquer número inteiro n quando n + ½ é representável.

A norma IEEE 754-2008 não fornece uma justificativa para os requisitos citados, no entanto, um rascunho inicial da seção relevante afirma:

Se o valor da função for zero, o sinal deste 0 é melhor determinado considerando a extensão contínua da função de sinal da função matemática.

A leitura do 754 Working Group Mail Archive pode gerar informações adicionais, mas não tive tempo de vasculhá-lo. Implementando sind(), cosd()e tand()conforme descrito acima, chegamos a esta tabela de casos de exemplo:

SIND
 angle value 
  -540 -0
  -360 -0
  -180 -0
     0  0
   180  0
   360  0
   540  0

COSD
 angle value
  -630  0
  -450  0
  -270  0
   -90  0
    90  0
   270  0
   450  0

TAND
 angle value
  -540  0
  -360 -0
  -180  0
     0  0
   180 -0
   360  0
   540 -0
njuffa
fonte
5

sin () e tan () normalmente retornam o mesmo resultado do sinal zero para uma entrada de sinal zero

Geralmente isso pode ser verdade desde:

  • Velocidade / precisão . Para duplas pequenas o suficiente, a melhor resposta sin(x)é x. Ou seja, para números menores que aproximadamente 1.49e-8, o dobro mais próximo do seno de x é na verdade o próprio x (veja o código-fonte da glibc para sin () ).

  • Tratamento de casos especiais .

    Algumas operações aritméticas extraordinárias são afetadas pelo sinal de zero; por exemplo "1/(+0) = +inf"mas "1/(-0) = -inf". Para manter sua utilidade, o bit de sinal deve se propagar através de certas operações aritméticas de acordo com regras derivadas de considerações de continuidade.

    Espera-se que implementações de funções transcendentais elementares como sin (z) e tan (z) e seus inversos e análogos hiperbólicos, embora não especificadas pelos padrões IEEE, sigam regras semelhantes. sin(z) Espera-se que azz = ±O implementação reproduza o sinal e o valor em .

    ( Cortes de ramo para funções elementares complexas ou muito barulho por pouco sinal de nada por W. Kahan)

    O zero assinado negativamente ecoa o conceito de análise matemática de se aproximar de 0 a partir de baixo como um limite unilateral (considere 1 / sin(x): o sinal de zero faz uma enorme diferença).

EDITAR

Considerando o segundo ponto, eu escreveria my_sindpara que:

my_sind(-0.0) is -0.0
my_sind(0.0) is 0.0

O padrão C mais recente (F.10.1.6 sine F.10.1.7 tan, implementações com zero assinado) especifica que, se o argumento for ±0, ele será retornado sem modificação .

EDIT 2

Para os outros valores, acho que é uma questão de aproximação. Dado M_PI<π:

0 = sin(π) < sin(M_PI)  1.2246467991473532e-16  +0.0
0 = sin(-π) > sin(-M_PI)  -1.2246467991473532e-16  -0.0
0 = sin(2*π) > sin(2*M_PI)  -2.4492935982947064e-16
0 = sin(-2*π) < sin(-2*M_PI)  2.4492935982947064e-16

Portanto, se my_sindfornecer respostas exatas em múltiplos de 180 °, ele poderá retornar +0.0ou -0.0(não vejo um motivo claro para preferir um ao outro).

Se my_sindusar alguma aproximação (por exemplo, uma degree * M_PI / 180.0fórmula de conversão), deve considerar como está se aproximando dos valores críticos.

manlio
fonte
Quais são os seus pensamentos sind(180), sind(-180), sind(360), sind(-360),...?
chux - Restabelece Monica
Obrigado pela atualização. Talvez minha postagem não esteja clara. A principal questão é my_trig(x)sempre voltar -0.0quando |x|não é 0.0?
chux - Restabelece Monica
Obrigado pelo "Então, se my_sind fornece respostas exatas em múltiplos de 180 °, ele pode retornar +0,0 ou -0,0 (não vejo uma razão clara para preferir uma sobre a outra)." É o ponto de discussão mais próximo até agora. Penso que o "princípio de menor espanto" encoraja sempre o retorno +0.0, mas procurando ver se há razões convincentes para retornar -0.0em algumas situações (além de x == +/-0.0).
chux - Restabelece Monica
@chux: Eu acho que para múltiplos de 180.0, realmente é preciso examinar os valores de precisão relativa da máquina, dados esses valores. Ou seja, o menor incremento / decremento que fornece um valor representável diferente nesse formato numérico. Em seguida, compare esse valor com o valor verdadeiro para ver se ele cairia no lado positivo ou negativo.
Rwong
@rwong Obrigado pela ideia. Múltiplos de 90,0 graus , a exigir sind(double degrees) e cosd(double degrees)valor pode ser devolvido: -1.0, +0.0, +1.0. Esta postagem é sobre deve -0.0sempre ser retornada (além de sind (-0.0)). Nota: sind()se não usar o simplista sin(x/360*M_PI)abordagem.
chux - Restabelece Monica 4/15
3

A biblioteca não tenta distinguir +0 de -0. O IEEE 754 se preocupa bastante com essa distinção ... Achei as funções [em math.h] bastante difíceis de escrever sem se preocupar com o sinal do nada. - PJ Plauger, The Standard C Library , 1992, página 128.

Formalmente, as funções trigonométricas devem retornar o sinal de zero de acordo com o padrão C ... que deixa o comportamento indefinido.

Em face do comportamento indefinido, o princípio de menor espanto sugere duplicar o comportamento da função correspondente de math.h. Isso cheira justificável, enquanto diverge do comportamento da função correspondente em math.hcheiros como uma maneira de introduzir bugs exatamente no código que depende do sinal de zero.

ben rudgers
fonte
As funções trig em math.hnão retornam 0.0 quando dados argumentos como +/- pi / 2 ou +/- pi, pois essas funções só podem ter valores representáveis próximos a +/- pi / 2, etc. Esses valores "próximos" retornam resultados próximos de 0,0. Como as funções trig da biblioteca std ( sin cos tan) não retornam 0,0 (ou -0,0) para nenhuma entrada (exceto +/- 0,0), mas my_sind (), my_cosd (), my_tand () podem retornar 0,0 (ou -0,0). nenhum comportamento de 0,0 para duplicar.
chux - Restabelece Monica
@chux A premissa que sin(-0.0)deve retornar -0é suspeita. Ele trata um detalhe de implementação do padrão IEEE como um princípio trigonométrico. Embora exista um princípio matemático geral de zero como o limite de dois intervalos incorporados na implementação do IEEE, ele ocorre nesse nível de abstração que não está na trigonometria geral [daí a variabilidade no retorno de suas funções trigonométricas]. O melhor que pode acontecer é que você pode definir uma convenção arbitrária, mas será divergente da math.hindiferença do indivíduo sobre o sinal de zero.
ben rudgers
Nota: Eu não estou sugerindo sin(-0.0)deve retornar -0.0, mas que my_sind(x)deve corresponder sin(x)quando xé +/-0.0. IOW: siga a prática anterior. Além disso, a pergunta em si é mais sobre o que fazer quando x != 0.0, my_sind(x)sempre deve retornar -0.0como em my_sind(180), etc? Talvez sua resposta / comentário trate disso - mas eu não vi isso.
chux - Restabelece Monica
@chux Se o comportamento é indefinido, é indefinido. É assim que C é. Plauger não se preocupou com +0versus -0quando ele escreveu math.hvinte anos atrás. Não está claro para mim qual é o problema que você está preocupando com a diferença.
amigos estão dizendo sobre ben rudgers
11
Espero que você veja que um bem implementado sin(rad)por qualquer valor rad>0e precisão nunca produzirá, 0.0pois pi é irracional. [Ref] (www.csee.umbc.edu/~phatak/645/supl/Ng-ArgReduction.pdf) No entanto, my_sind(deg)produz um exato 0.0(ou + ou -) todo múltiplo de 180.0como o valor 0,0 é o resultado matemático correto. "Princípio de menor espanto" sugere retornar 0,0 nesses casos. Minha pergunta deve -0.0ser retornada nesses casos?
chux - Restabelece Monica