¿Por qué escribir 1,000,000,000 como 1000 * 1000 * 1000 en C?

142

En el código creado por Apple, existe esta línea:

CMTimeMakeWithSeconds( newDurationSeconds, 1000*1000*1000 )

¿Hay alguna razón para expresar 1,000,000,000como 1000*1000*1000?

¿Por qué no 1000^3para el caso?

Pato
fuente
45
Claridad y legibilidad del código. No puede poner, o 'como separadores en C, por lo que lo mejor es calcular el valor de una multiplicación. ^ es un operador no relacionado en C - OR exclusivo.
Iwillnotexist Idonotexist
54
se usa especialmente para duraciones como: 2 * 60 * 60 .. es fácil notar que es por 2 horas ..
Idali
53
Por un lado, sospecho que 1,000,000,000 no es una sintaxis válida
paparazzo
15
Por cierto, los lenguajes de programación modernos tienen una forma alternativa de escribir lo mismo, por ejemplo, en Swift 1_000_000_000. Sin embargo, con constantes de tiempo esto es más difícil. Es más legible escribir 30 * 60(30 minutos en segundos) que escribir 1800. En realidad, hay idiomas que le permitirán escribir unidades, por ejemplo meters, permitiéndole protegerse contra las malas asignaciones.
Sulthan
29
^es un XOR, no un exponente u operador de potencia.
3Dave

Respuestas:

200

Una razón para declarar constantes de forma multiplicativa es mejorar la legibilidad, mientras que el rendimiento en tiempo de ejecución no se ve afectado. Además, para indicar que el escritor estaba pensando de manera multiplicativa sobre el número.

Considera esto:

double memoryBytes = 1024 * 1024 * 1024;

Es claramente mejor que:

double memoryBytes = 1073741824;

como este último no parece, a primera vista, la tercera potencia de 1024.

Como mencionó Amin Negm-Awad, el ^operador es el binario XOR. Muchos idiomas carecen del operador de exponenciación de tiempo de compilación incorporado, de ahí la multiplicación.

Piotr Falkowski
fuente
13
Y en los idiomas que tienen un operador de exponenciación, no es necesariamente '^'. En Fortran, por ejemplo, es '**'.
jamesqf
44
También debe incluir un enlace que apunte a la advertencia importante, dada en la respuesta a continuación, de @chux: stackoverflow.com/a/40637622/1841533 (especialmente como el OP etiquetado "c", que es muy susceptible a esta "mano derecha") la operación lateral parece tener todos los términos limitados a un tipo más pequeño y, por lo tanto, la multiplicación puede desbordar el problema). securecoding.cert.org/confluence/display/c/… ¿ puede ayudar a evitarlos en el caso general?
Olivier Dulac
44
También debemos tener en cuenta que el cálculo se realiza en tiempo de compilación. El Estándar C requiere que la implementación pueda calcular expresiones constantes en tiempo de compilación para varias características del lenguaje y podemos asumir con seguridad que es cierto cuando se usa una expresión constante como en este ejemplo.
ouah
13
¿Almacenar la cantidad de memoria como un doble? Eso parece una fuente potencial de error.
JAB
77
@supercat Soy consciente de eso, pero al usar el doble podría tener un caso en el que, por ejemplo, desea una parte del rango de memoria, se divide por xpara obtener el tamaño del subrango ... y de repente tiene una fracción byte, que puede requerir lógica adicional para compensar.
JAB
73

¿Por qué no 1000^3?

El resultado de 1000^3es 1003. ^es el operador bit-XOR.

Incluso si no trata con la Q en sí, agrego una aclaración. x^yno no siempre evaluar a x+ycomo lo hace en el ejemplo de la pregunta. Tienes que xor cada bit. En el caso del ejemplo:

1111101000 (1000₁₀)
0000000011 (3₁₀)
1111101011 (1003₁₀)

Pero

1111101001 (1001₁₀)
0000000011 (3₁₀)
1111101010 (1002₁₀)
Amin Negm-Awad
fuente
3
señor, no estoy claro cómo 1003 ^ 3 es 1003. Google y Mac Calculator muestran 1000 ^ 3 = 1,000,000,000. ¿puedes explicar?
Mahesh
57
El ^operador significa XOR en C / C ++ / Objective-C, etc. En las calculadoras generalmente significa potencia x-to-the-y.
Tamás Zahola
55
Bah, los bits de 1000 y 3 no se superponen. Esto se ve muy mal.
Yakk - Adam Nevraumont
19
Los bits se superponen. Pero no los 1's. : -]
Amin Negm-Awad
66
@Yakk: de hecho, ¡se ve tan mal! ... Espero que muchas personas no piensen que "A ^ B" siempre da A + B (pero me temo que algo podría ...)
Olivier Dulac
71

Hay razones para no usar 1000 * 1000 * 1000.

Con 16 bits int, 1000 * 1000desbordamientos. Por lo tanto, el uso 1000 * 1000 * 1000reduce la portabilidad.

Con 32 bits int, la siguiente primera línea de código se desborda.

long long Duration = 1000 * 1000 * 1000 * 1000;  // overflow
long long Duration = 1000000000000;  // no overflow, hard to read

Sugiera que el valor inicial coincida con el tipo de destino para facilitar la lectura, la portabilidad y la corrección.

double Duration = 1000.0 * 1000 * 1000;
long long Duration = 1000LL * 1000 * 1000 * 1000;

También podría usar enotación simple para valores que son exactamente representables como a double. Por supuesto, esto lleva a saber si doublepuede representar exactamente el valor del número entero, algo preocupante con valores superiores a 1e9. (Ver DBL_EPSILONy DBL_DIG).

long Duration = 1000000000;
// vs.
long Duration = 1e9;
chux - Restablecer Monica
fuente
55
Comentario muy importante! securecoding.cert.org/confluence/display/c/… puede ayudar en muchos casos?
Olivier Dulac
2
A doublepuede representar exactamente todos los enteros hasta 2 ^ 53 ≈ 9e15.
Edgar Bonet
3
@EdgarBonet Es cierto que binary64 puede representar un número entero hasta aproximadamente 9e15. Pero C no especifica el doubleuso de binary64, a pesar de que se usa con mucha frecuencia. Según la especificación C, los valores de hasta 1e9 más o menos son exactamente representables. Depende si desea codificar para especificar o confiar en la práctica común.
chux - Restablece a Monica el
44
@Patrick Ambos 1000y 1000000000000son constantes enteras . Cada uno independientemente tiene el tipo seleccionado de int, longo long long. El compilador usa el primer tipo de esos 3 en los que se ajusta la constante entera . 1000 * 1000 * 1000 * 1000se hace con las intmatemáticas como cada 1000una en un int. El producto se desborda con 32 bits int. 1000000000000 es ciertamente representable como un long long(o posiblemente más estrecho), sin desbordamiento. El tipo de objetivo long long Durationno afecta a este "lado derecho de la =" detección.
chux - Restablece a Monica el
2
Colocar el tipo más ancho primero en la multiplicación es importante. Con 16 bits int, long x = 1000 * 1000 * 1000L;se desbordaría, mientras long x = 1000L * 1000 * 1000;que no.
ex nihilo
51

Por legibilidad.

Colocar comas y espacios entre los ceros ( 1 000 000 000o 1,000,000,000) produciría un error de sintaxis, y tener 1000000000en el código hace que sea difícil ver exactamente cuántos ceros hay.

1000*1000*1000hace evidente que son 10 ^ 9, porque nuestros ojos pueden procesar los trozos más fácilmente. Además, no hay costo de tiempo de ejecución, porque el compilador lo reemplazará con la constante 1000000000.

Tamás Zahola
fuente
55
Para su información, hay un concepto de separadores de dígitos que aprendí recientemente. Java lo ha tenido por un tiempo ahora, y C # 7.0 puede obtenerlo. Desearía que todos los idiomas tengan esta característica llamativa. :)
iheartcsharp
1
Dependiendo del contexto, el uso 1,000,000,000no produciría un error de sintaxis, solo significaría algo más. Por ejemploCMTimeMakeWithSeconds( newDurationSeconds, 1,000,000,000 )
Ross Ridge, el
2
@ JMS10 C # ya lo tiene si instala la versión de vista previa VS15, puede escribirse como1_000_000_000
user1306322
2
Python también se está _separando :)
Wayne Werner
2
Y C ++ obtuvo recientemente el 'separador, en C ++ 14, para que pueda usarlo 1'000'000'000. (Se eligió porque 1,000,000,000podría malinterpretarse como el operador de coma o 4 parámetros distintos, y _1_000_000_000es un nombre de variable válido (pero probablemente malo)).
Justin Time - Restablece a Monica el
27

Por legibilidad. A modo de comparación, Java admite _números para mejorar la legibilidad (propuesta por primera vez por Stephen Colebourne como respuesta a la PROPUESTA de Derek Foster: Literales binarios para Project Coin / JSR 334). Uno escribiría 1_000_000_000aquí.

En un orden aproximadamente cronológico, desde el soporte más antiguo hasta el más nuevo:

Es una característica relativamente nueva para los idiomas darse cuenta de que deberían ser compatibles (y luego está Perl). Como en la excelente respuesta de chux @, 1000*1000...es una solución parcial pero abre el programador a errores que desbordan la multiplicación incluso si el resultado final es de tipo grande.

djechlin
fuente
Muchos lenguajes de programación modernos tienen lo mismo, por ejemplo, Swift. Nada nuevo.
Sulthan
AFAIK, esto viene de Perl. PL / M usó $ para el mismo propósito, por ejemplo: 0100 $ 0010B
ninjalj
1
Sin embargo, es bastante nuevo. La característica de Java tiene quizás 5 años. La mayoría de los otros idiomas que admiten esta sintaxis son bastante nuevos: Swift tiene solo unos pocos años. Python agrega soporte en 3.6, que aún no se ha lanzado.
johncip
2
Ada ha apoyado subrayados en literales enteros durante 33 años.
Peter - Restablece a Monica el
1
@djechlin: Me he tomado la libertad de agregar más información, aproximadamente en orden cronológico. Me equivoqué antes, a juzgar por el hilo de Project Coin, Stephen Colebourne probablemente tomó la idea de subrayar en literales enteros de Fandom y / o Ruby. Ruby probablemente tomó la idea de Perl y Perl de Ada.
ninjalj
7

Puede ser más simple de leer y obtener algunas asociaciones con el 1,000,000,000formulario.

Desde el punto de vista técnico, supongo que no hay diferencia entre el número directo o la multiplicación. El compilador lo generará como un número constante de mil millones de todos modos.

Si habla sobre el objetivo-c, entonces 1000^3no funcionará porque no existe tal sintaxis para pow (es xor). En cambio, pow()se puede usar la función. Pero en ese caso, no será óptimo, será una llamada de función de tiempo de ejecución y no una constante generada por el compilador.

Madars Vi
fuente
3

Para ilustrar las razones, considere el siguiente programa de prueba:

$ cat comma-expr.c && gcc -o comma-expr comma-expr.c && ./comma-expr
#include <stdio.h>

#define BILLION1 (1,000,000,000)
#define BILLION2 (1000^3)

int main()
{
        printf("%d, %d\n", BILLION1, BILLION2);
}
0, 1003
$
Peter - Restablece a Monica
fuente
1
@pjvandehaar No recomendaría aprender un idioma leyendo artículos de Wikipedia.
Peter - Restablece a Mónica el
0

Otra forma de lograr un efecto similar en C para los números decimales es usar la notación literal de coma flotante, siempre que un doble pueda representar el número que desee sin pérdida de precisión.

IEEE 754 doble de 64 bits puede representar cualquier número entero no negativo <= 2 ^ 53 sin problema. Por lo general, el doble largo (80 o 128 bits) puede ir aún más lejos que eso. Las conversiones se realizarán en tiempo de compilación, por lo que no hay sobrecarga de tiempo de ejecución y es probable que reciba advertencias si hay una pérdida inesperada de precisión y tiene un buen compilador.

long lots_of_secs = 1e9;
jschultz410
fuente